Import xenbus sources, from linux sparse tree in the 20060107 xen-3.0

snapshot.
This commit is contained in:
bouyer 2006-03-06 20:16:33 +00:00
parent df06d5bae0
commit ed3fe49dd5
7 changed files with 2611 additions and 0 deletions

View File

@ -0,0 +1,8 @@
obj-y += xenbus.o
xenbus-objs =
xenbus-objs += xenbus_client.o
xenbus-objs += xenbus_comms.o
xenbus-objs += xenbus_xs.o
xenbus-objs += xenbus_probe.o
xenbus-objs += xenbus_dev.o

View File

@ -0,0 +1,256 @@
/******************************************************************************
* Client-facing interface for the Xenbus driver. In other words, the
* interface between the Xenbus and the device-specific code, be it the
* frontend or the backend of that driver.
*
* Copyright (C) 2005 XenSource Ltd
*
* This file may be 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.
*/
#if 0
#define DPRINTK(fmt, args...) \
printk("xenbus_client (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
#else
#define DPRINTK(fmt, args...) ((void)0)
#endif
#include <asm-xen/evtchn.h>
#include <asm-xen/gnttab.h>
#include <asm-xen/xenbus.h>
int xenbus_watch_path(struct xenbus_device *dev, const char *path,
struct xenbus_watch *watch,
void (*callback)(struct xenbus_watch *,
const char **, unsigned int))
{
int err;
watch->node = path;
watch->callback = callback;
err = register_xenbus_watch(watch);
if (err) {
watch->node = NULL;
watch->callback = NULL;
xenbus_dev_fatal(dev, err, "adding watch on %s", path);
}
return err;
}
EXPORT_SYMBOL(xenbus_watch_path);
int xenbus_watch_path2(struct xenbus_device *dev, const char *path,
const char *path2, struct xenbus_watch *watch,
void (*callback)(struct xenbus_watch *,
const char **, unsigned int))
{
int err;
char *state =
kmalloc(strlen(path) + 1 + strlen(path2) + 1, GFP_KERNEL);
if (!state) {
xenbus_dev_fatal(dev, -ENOMEM, "allocating path for watch");
return -ENOMEM;
}
strcpy(state, path);
strcat(state, "/");
strcat(state, path2);
err = xenbus_watch_path(dev, state, watch, callback);
if (err) {
kfree(state);
}
return err;
}
EXPORT_SYMBOL(xenbus_watch_path2);
int xenbus_switch_state(struct xenbus_device *dev,
struct xenbus_transaction *xbt,
XenbusState state)
{
/* We check whether the state is currently set to the given value, and
if not, then the state is set. We don't want to unconditionally
write the given state, because we don't want to fire watches
unnecessarily. Furthermore, if the node has gone, we don't write
to it, as the device will be tearing down, and we don't want to
resurrect that directory.
*/
int current_state;
int err = xenbus_scanf(xbt, dev->nodename, "state", "%d",
&current_state);
if ((err == 1 && (XenbusState)current_state == state) ||
err == -ENOENT)
return 0;
err = xenbus_printf(xbt, dev->nodename, "state", "%d", state);
if (err) {
xenbus_dev_fatal(dev, err, "writing new state");
return err;
}
return 0;
}
EXPORT_SYMBOL(xenbus_switch_state);
/**
* Return the path to the error node for the given device, or NULL on failure.
* If the value returned is non-NULL, then it is the caller's to kfree.
*/
static char *error_path(struct xenbus_device *dev)
{
char *path_buffer = kmalloc(strlen("error/") + strlen(dev->nodename) +
1, GFP_KERNEL);
if (path_buffer == NULL) {
return NULL;
}
strcpy(path_buffer, "error/");
strcpy(path_buffer + strlen("error/"), dev->nodename);
return path_buffer;
}
void _dev_error(struct xenbus_device *dev, int err, const char *fmt,
va_list ap)
{
int ret;
unsigned int len;
char *printf_buffer = NULL, *path_buffer = NULL;
#define PRINTF_BUFFER_SIZE 4096
printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
if (printf_buffer == NULL)
goto fail;
len = sprintf(printf_buffer, "%i ", -err);
ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
dev->has_error = 1;
path_buffer = error_path(dev);
if (path_buffer == NULL) {
printk("xenbus: failed to write error node for %s (%s)\n",
dev->nodename, printf_buffer);
goto fail;
}
if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
printk("xenbus: failed to write error node for %s (%s)\n",
dev->nodename, printf_buffer);
goto fail;
}
fail:
if (printf_buffer)
kfree(printf_buffer);
if (path_buffer)
kfree(path_buffer);
}
void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,
...)
{
va_list ap;
va_start(ap, fmt);
_dev_error(dev, err, fmt, ap);
va_end(ap);
}
EXPORT_SYMBOL(xenbus_dev_error);
void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt,
...)
{
va_list ap;
va_start(ap, fmt);
_dev_error(dev, err, fmt, ap);
va_end(ap);
xenbus_switch_state(dev, NULL, XenbusStateClosing);
}
EXPORT_SYMBOL(xenbus_dev_fatal);
int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn)
{
int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0);
if (err < 0)
xenbus_dev_fatal(dev, err, "granting access to ring page");
return err;
}
EXPORT_SYMBOL(xenbus_grant_ring);
int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
{
evtchn_op_t op = {
.cmd = EVTCHNOP_alloc_unbound,
.u.alloc_unbound.dom = DOMID_SELF,
.u.alloc_unbound.remote_dom = dev->otherend_id };
int err = HYPERVISOR_event_channel_op(&op);
if (err)
xenbus_dev_fatal(dev, err, "allocating event channel");
else
*port = op.u.alloc_unbound.port;
return err;
}
EXPORT_SYMBOL(xenbus_alloc_evtchn);
XenbusState xenbus_read_driver_state(const char *path)
{
XenbusState result;
int err = xenbus_gather(NULL, path, "state", "%d", &result, NULL);
if (err)
result = XenbusStateClosed;
return result;
}
EXPORT_SYMBOL(xenbus_read_driver_state);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/

View File

@ -0,0 +1,203 @@
/******************************************************************************
* xenbus_comms.c
*
* Low level code to talks to Xen Store: ringbuffer and event channel.
*
* Copyright (C) 2005 Rusty Russell, IBM Corporation
*
* This file may be 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.
*/
#include <asm/hypervisor.h>
#include <asm-xen/evtchn.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/err.h>
#include <asm-xen/xenbus.h>
#include "xenbus_comms.h"
static int xenbus_irq;
extern void xenbus_probe(void *);
extern int xenstored_ready;
static DECLARE_WORK(probe_work, xenbus_probe, NULL);
DECLARE_WAIT_QUEUE_HEAD(xb_waitq);
static inline struct xenstore_domain_interface *xenstore_domain_interface(void)
{
return mfn_to_virt(xen_start_info->store_mfn);
}
static irqreturn_t wake_waiting(int irq, void *unused, struct pt_regs *regs)
{
if (unlikely(xenstored_ready == 0)) {
xenstored_ready = 1;
schedule_work(&probe_work);
}
wake_up(&xb_waitq);
return IRQ_HANDLED;
}
static int check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
{
return ((prod - cons) <= XENSTORE_RING_SIZE);
}
static void *get_output_chunk(XENSTORE_RING_IDX cons,
XENSTORE_RING_IDX prod,
char *buf, uint32_t *len)
{
*len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
*len = XENSTORE_RING_SIZE - (prod - cons);
return buf + MASK_XENSTORE_IDX(prod);
}
static const void *get_input_chunk(XENSTORE_RING_IDX cons,
XENSTORE_RING_IDX prod,
const char *buf, uint32_t *len)
{
*len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
if ((prod - cons) < *len)
*len = prod - cons;
return buf + MASK_XENSTORE_IDX(cons);
}
int xb_write(const void *data, unsigned len)
{
struct xenstore_domain_interface *intf = xenstore_domain_interface();
XENSTORE_RING_IDX cons, prod;
while (len != 0) {
void *dst;
unsigned int avail;
wait_event_interruptible(xb_waitq,
(intf->req_prod - intf->req_cons) !=
XENSTORE_RING_SIZE);
/* Read indexes, then verify. */
cons = intf->req_cons;
prod = intf->req_prod;
mb();
if (!check_indexes(cons, prod))
return -EIO;
dst = get_output_chunk(cons, prod, intf->req, &avail);
if (avail == 0)
continue;
if (avail > len)
avail = len;
memcpy(dst, data, avail);
data += avail;
len -= avail;
/* Other side must not see new header until data is there. */
wmb();
intf->req_prod += avail;
/* This implies mb() before other side sees interrupt. */
notify_remote_via_evtchn(xen_start_info->store_evtchn);
}
return 0;
}
int xb_read(void *data, unsigned len)
{
struct xenstore_domain_interface *intf = xenstore_domain_interface();
XENSTORE_RING_IDX cons, prod;
while (len != 0) {
unsigned int avail;
const char *src;
wait_event_interruptible(xb_waitq,
intf->rsp_cons != intf->rsp_prod);
/* Read indexes, then verify. */
cons = intf->rsp_cons;
prod = intf->rsp_prod;
mb();
if (!check_indexes(cons, prod))
return -EIO;
src = get_input_chunk(cons, prod, intf->rsp, &avail);
if (avail == 0)
continue;
if (avail > len)
avail = len;
/* We must read header before we read data. */
rmb();
memcpy(data, src, avail);
data += avail;
len -= avail;
/* Other side must not see free space until we've copied out */
mb();
intf->rsp_cons += avail;
pr_debug("Finished read of %i bytes (%i to go)\n", avail, len);
/* Implies mb(): they will see new header. */
notify_remote_via_evtchn(xen_start_info->store_evtchn);
}
return 0;
}
/* Set up interrupt handler off store event channel. */
int xb_init_comms(void)
{
int err;
if (xenbus_irq)
unbind_from_irqhandler(xenbus_irq, &xb_waitq);
err = bind_evtchn_to_irqhandler(
xen_start_info->store_evtchn, wake_waiting,
0, "xenbus", &xb_waitq);
if (err <= 0) {
printk(KERN_ERR "XENBUS request irq failed %i\n", err);
return err;
}
xenbus_irq = err;
return 0;
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/

View File

@ -0,0 +1,50 @@
/*
* Private include for xenbus communications.
*
* Copyright (C) 2005 Rusty Russell, IBM Corporation
*
* This file may be 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.
*/
#ifndef _XENBUS_COMMS_H
#define _XENBUS_COMMS_H
int xs_init(void);
int xb_init_comms(void);
/* Low level routines. */
int xb_write(const void *data, unsigned len);
int xb_read(void *data, unsigned len);
int xs_input_avail(void);
extern wait_queue_head_t xb_waitq;
#endif /* _XENBUS_COMMS_H */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/

View File

@ -0,0 +1,241 @@
/*
* xenbus_dev.c
*
* 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 file may be 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.
*/
#include <linux/config.h>
#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 "xenbus_comms.h"
#include <asm/uaccess.h>
#include <asm/hypervisor.h>
#include <asm-xen/xenbus.h>
#include <asm-xen/xen_proc.h>
#include <asm/hypervisor.h>
struct xenbus_dev_transaction {
struct list_head list;
struct xenbus_transaction *handle;
};
struct xenbus_dev_data {
/* In-progress transaction. */
struct list_head transactions;
/* Partial request. */
unsigned int len;
union {
struct xsd_sockmsg msg;
char buffer[PAGE_SIZE];
} u;
/* Response queue. */
#define MASK_READ_IDX(idx) ((idx)&(PAGE_SIZE-1))
char read_buffer[PAGE_SIZE];
unsigned int read_cons, read_prod;
wait_queue_head_t read_waitq;
};
static struct proc_dir_entry *xenbus_dev_intf;
static ssize_t xenbus_dev_read(struct file *filp,
char __user *ubuf,
size_t len, loff_t *ppos)
{
struct xenbus_dev_data *u = filp->private_data;
int i;
if (wait_event_interruptible(u->read_waitq,
u->read_prod != u->read_cons))
return -EINTR;
for (i = 0; i < len; i++) {
if (u->read_cons == u->read_prod)
break;
put_user(u->read_buffer[MASK_READ_IDX(u->read_cons)], ubuf+i);
u->read_cons++;
}
return i;
}
static void queue_reply(struct xenbus_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];
BUG_ON((u->read_prod - u->read_cons) > sizeof(u->read_buffer));
wake_up(&u->read_waitq);
}
static ssize_t xenbus_dev_write(struct file *filp,
const char __user *ubuf,
size_t len, loff_t *ppos)
{
struct xenbus_dev_data *u = filp->private_data;
struct xenbus_dev_transaction *trans;
void *reply;
int err = 0;
if ((len + u->len) > sizeof(u->u.buffer))
return -EINVAL;
if (copy_from_user(u->u.buffer + u->len, ubuf, len) != 0)
return -EFAULT;
u->len += len;
if (u->len < (sizeof(u->u.msg) + u->u.msg.len))
return len;
switch (u->u.msg.type) {
case XS_TRANSACTION_START:
case XS_TRANSACTION_END:
case XS_DIRECTORY:
case XS_READ:
case XS_GET_PERMS:
case XS_RELEASE:
case XS_GET_DOMAIN_PATH:
case XS_WRITE:
case XS_MKDIR:
case XS_RM:
case XS_SET_PERMS:
reply = xenbus_dev_request_and_reply(&u->u.msg);
if (IS_ERR(reply)) {
err = PTR_ERR(reply);
} else {
if (u->u.msg.type == XS_TRANSACTION_START) {
trans = kmalloc(sizeof(*trans), GFP_KERNEL);
trans->handle = (struct xenbus_transaction *)
simple_strtoul(reply, NULL, 0);
list_add(&trans->list, &u->transactions);
} else if (u->u.msg.type == XS_TRANSACTION_END) {
list_for_each_entry(trans, &u->transactions,
list)
if ((unsigned long)trans->handle ==
(unsigned long)u->u.msg.tx_id)
break;
BUG_ON(&trans->list == &u->transactions);
list_del(&trans->list);
kfree(trans);
}
queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg));
queue_reply(u, (char *)reply, u->u.msg.len);
kfree(reply);
}
break;
default:
err = -EINVAL;
break;
}
if (err == 0) {
u->len = 0;
err = len;
}
return err;
}
static int xenbus_dev_open(struct inode *inode, struct file *filp)
{
struct xenbus_dev_data *u;
if (xen_start_info->store_evtchn == 0)
return -ENOENT;
nonseekable_open(inode, filp);
u = kmalloc(sizeof(*u), GFP_KERNEL);
if (u == NULL)
return -ENOMEM;
memset(u, 0, sizeof(*u));
INIT_LIST_HEAD(&u->transactions);
init_waitqueue_head(&u->read_waitq);
filp->private_data = u;
return 0;
}
static int xenbus_dev_release(struct inode *inode, struct file *filp)
{
struct xenbus_dev_data *u = filp->private_data;
struct xenbus_dev_transaction *trans, *tmp;
list_for_each_entry_safe(trans, tmp, &u->transactions, list) {
xenbus_transaction_end(trans->handle, 1);
list_del(&trans->list);
kfree(trans);
}
kfree(u);
return 0;
}
static struct file_operations xenbus_dev_file_ops = {
.read = xenbus_dev_read,
.write = xenbus_dev_write,
.open = xenbus_dev_open,
.release = xenbus_dev_release,
};
static int __init
xenbus_dev_init(void)
{
xenbus_dev_intf = create_xen_proc_entry("xenbus", 0400);
if (xenbus_dev_intf)
xenbus_dev_intf->proc_fops = &xenbus_dev_file_ops;
return 0;
}
__initcall(xenbus_dev_init);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,827 @@
/******************************************************************************
* xenbus_xs.c
*
* This is the kernel equivalent of the "xs" library. We don't need everything
* and we use xenbus_comms for communication.
*
* Copyright (C) 2005 Rusty Russell, IBM Corporation
*
* This file may be 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.
*/
#include <linux/unistd.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/uio.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/kthread.h>
#include <asm-xen/xenbus.h>
#include "xenbus_comms.h"
#define streq(a, b) (strcmp((a), (b)) == 0)
struct xs_stored_msg {
struct list_head list;
struct xsd_sockmsg hdr;
union {
/* Queued replies. */
struct {
char *body;
} reply;
/* Queued watch events. */
struct {
struct xenbus_watch *handle;
char **vec;
unsigned int vec_size;
} watch;
} u;
};
struct xs_handle {
/* A list of replies. Currently only one will ever be outstanding. */
struct list_head reply_list;
spinlock_t reply_lock;
wait_queue_head_t reply_waitq;
/* One request at a time. */
struct semaphore request_mutex;
/* Protect transactions against save/restore. */
struct rw_semaphore suspend_mutex;
};
static struct xs_handle xs_state;
/* List of registered watches, and a lock to protect it. */
static LIST_HEAD(watches);
static DEFINE_SPINLOCK(watches_lock);
/* List of pending watch callback events, and a lock to protect it. */
static LIST_HEAD(watch_events);
static DEFINE_SPINLOCK(watch_events_lock);
/*
* Details of the xenwatch callback kernel thread. The thread waits on the
* watch_events_waitq for work to do (queued on watch_events list). When it
* wakes up it acquires the xenwatch_mutex before reading the list and
* carrying out work.
*/
static pid_t xenwatch_pid;
/* static */ DECLARE_MUTEX(xenwatch_mutex);
static DECLARE_WAIT_QUEUE_HEAD(watch_events_waitq);
static int get_error(const char *errorstring)
{
unsigned int i;
for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) {
if (i == ARRAY_SIZE(xsd_errors) - 1) {
printk(KERN_WARNING
"XENBUS xen store gave: unknown error %s",
errorstring);
return EINVAL;
}
}
return xsd_errors[i].errnum;
}
static void *read_reply(enum xsd_sockmsg_type *type, unsigned int *len)
{
struct xs_stored_msg *msg;
char *body;
spin_lock(&xs_state.reply_lock);
while (list_empty(&xs_state.reply_list)) {
spin_unlock(&xs_state.reply_lock);
wait_event_interruptible(xs_state.reply_waitq,
!list_empty(&xs_state.reply_list));
spin_lock(&xs_state.reply_lock);
}
msg = list_entry(xs_state.reply_list.next,
struct xs_stored_msg, list);
list_del(&msg->list);
spin_unlock(&xs_state.reply_lock);
*type = msg->hdr.type;
if (len)
*len = msg->hdr.len;
body = msg->u.reply.body;
kfree(msg);
return body;
}
/* Emergency write. */
void xenbus_debug_write(const char *str, unsigned int count)
{
struct xsd_sockmsg msg = { 0 };
msg.type = XS_DEBUG;
msg.len = sizeof("print") + count + 1;
down(&xs_state.request_mutex);
xb_write(&msg, sizeof(msg));
xb_write("print", sizeof("print"));
xb_write(str, count);
xb_write("", 1);
up(&xs_state.request_mutex);
}
void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)
{
void *ret;
struct xsd_sockmsg req_msg = *msg;
int err;
if (req_msg.type == XS_TRANSACTION_START)
down_read(&xs_state.suspend_mutex);
down(&xs_state.request_mutex);
err = xb_write(msg, sizeof(*msg) + msg->len);
if (err) {
msg->type = XS_ERROR;
ret = ERR_PTR(err);
} else {
ret = read_reply(&msg->type, &msg->len);
}
up(&xs_state.request_mutex);
if ((msg->type == XS_TRANSACTION_END) ||
((req_msg.type == XS_TRANSACTION_START) &&
(msg->type == XS_ERROR)))
up_read(&xs_state.suspend_mutex);
return ret;
}
/* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */
static void *xs_talkv(struct xenbus_transaction *t,
enum xsd_sockmsg_type type,
const struct kvec *iovec,
unsigned int num_vecs,
unsigned int *len)
{
struct xsd_sockmsg msg;
void *ret = NULL;
unsigned int i;
int err;
msg.tx_id = (u32)(unsigned long)t;
msg.req_id = 0;
msg.type = type;
msg.len = 0;
for (i = 0; i < num_vecs; i++)
msg.len += iovec[i].iov_len;
down(&xs_state.request_mutex);
err = xb_write(&msg, sizeof(msg));
if (err) {
up(&xs_state.request_mutex);
return ERR_PTR(err);
}
for (i = 0; i < num_vecs; i++) {
err = xb_write(iovec[i].iov_base, iovec[i].iov_len);;
if (err) {
up(&xs_state.request_mutex);
return ERR_PTR(err);
}
}
ret = read_reply(&msg.type, len);
up(&xs_state.request_mutex);
if (IS_ERR(ret))
return ret;
if (msg.type == XS_ERROR) {
err = get_error(ret);
kfree(ret);
return ERR_PTR(-err);
}
BUG_ON(msg.type != type);
return ret;
}
/* Simplified version of xs_talkv: single message. */
static void *xs_single(struct xenbus_transaction *t,
enum xsd_sockmsg_type type,
const char *string,
unsigned int *len)
{
struct kvec iovec;
iovec.iov_base = (void *)string;
iovec.iov_len = strlen(string) + 1;
return xs_talkv(t, type, &iovec, 1, len);
}
/* Many commands only need an ack, don't care what it says. */
static int xs_error(char *reply)
{
if (IS_ERR(reply))
return PTR_ERR(reply);
kfree(reply);
return 0;
}
static unsigned int count_strings(const char *strings, unsigned int len)
{
unsigned int num;
const char *p;
for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1)
num++;
return num;
}
/* Return the path to dir with /name appended. Buffer must be kfree()'ed. */
static char *join(const char *dir, const char *name)
{
char *buffer;
buffer = kmalloc(strlen(dir) + strlen("/") + strlen(name) + 1,
GFP_KERNEL);
if (buffer == NULL)
return ERR_PTR(-ENOMEM);
strcpy(buffer, dir);
if (!streq(name, "")) {
strcat(buffer, "/");
strcat(buffer, name);
}
return buffer;
}
static char **split(char *strings, unsigned int len, unsigned int *num)
{
char *p, **ret;
/* Count the strings. */
*num = count_strings(strings, len);
/* Transfer to one big alloc for easy freeing. */
ret = kmalloc(*num * sizeof(char *) + len, GFP_KERNEL);
if (!ret) {
kfree(strings);
return ERR_PTR(-ENOMEM);
}
memcpy(&ret[*num], strings, len);
kfree(strings);
strings = (char *)&ret[*num];
for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
ret[(*num)++] = p;
return ret;
}
char **xenbus_directory(struct xenbus_transaction *t,
const char *dir, const char *node, unsigned int *num)
{
char *strings, *path;
unsigned int len;
path = join(dir, node);
if (IS_ERR(path))
return (char **)path;
strings = xs_single(t, XS_DIRECTORY, path, &len);
kfree(path);
if (IS_ERR(strings))
return (char **)strings;
return split(strings, len, num);
}
EXPORT_SYMBOL(xenbus_directory);
/* Check if a path exists. Return 1 if it does. */
int xenbus_exists(struct xenbus_transaction *t,
const char *dir, const char *node)
{
char **d;
int dir_n;
d = xenbus_directory(t, dir, node, &dir_n);
if (IS_ERR(d))
return 0;
kfree(d);
return 1;
}
EXPORT_SYMBOL(xenbus_exists);
/* Get the value of a single file.
* Returns a kmalloced value: call free() on it after use.
* len indicates length in bytes.
*/
void *xenbus_read(struct xenbus_transaction *t,
const char *dir, const char *node, unsigned int *len)
{
char *path;
void *ret;
path = join(dir, node);
if (IS_ERR(path))
return (void *)path;
ret = xs_single(t, XS_READ, path, len);
kfree(path);
return ret;
}
EXPORT_SYMBOL(xenbus_read);
/* Write the value of a single file.
* Returns -err on failure.
*/
int xenbus_write(struct xenbus_transaction *t,
const char *dir, const char *node, const char *string)
{
const char *path;
struct kvec iovec[2];
int ret;
path = join(dir, node);
if (IS_ERR(path))
return PTR_ERR(path);
iovec[0].iov_base = (void *)path;
iovec[0].iov_len = strlen(path) + 1;
iovec[1].iov_base = (void *)string;
iovec[1].iov_len = strlen(string);
ret = xs_error(xs_talkv(t, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL));
kfree(path);
return ret;
}
EXPORT_SYMBOL(xenbus_write);
/* Create a new directory. */
int xenbus_mkdir(struct xenbus_transaction *t,
const char *dir, const char *node)
{
char *path;
int ret;
path = join(dir, node);
if (IS_ERR(path))
return PTR_ERR(path);
ret = xs_error(xs_single(t, XS_MKDIR, path, NULL));
kfree(path);
return ret;
}
EXPORT_SYMBOL(xenbus_mkdir);
/* Destroy a file or directory (directories must be empty). */
int xenbus_rm(struct xenbus_transaction *t, const char *dir, const char *node)
{
char *path;
int ret;
path = join(dir, node);
if (IS_ERR(path))
return PTR_ERR(path);
ret = xs_error(xs_single(t, XS_RM, path, NULL));
kfree(path);
return ret;
}
EXPORT_SYMBOL(xenbus_rm);
/* Start a transaction: changes by others will not be seen during this
* transaction, and changes will not be visible to others until end.
*/
struct xenbus_transaction *xenbus_transaction_start(void)
{
char *id_str;
unsigned long id;
down_read(&xs_state.suspend_mutex);
id_str = xs_single(NULL, XS_TRANSACTION_START, "", NULL);
if (IS_ERR(id_str)) {
up_read(&xs_state.suspend_mutex);
return (struct xenbus_transaction *)id_str;
}
id = simple_strtoul(id_str, NULL, 0);
kfree(id_str);
return (struct xenbus_transaction *)id;
}
EXPORT_SYMBOL(xenbus_transaction_start);
/* End a transaction.
* If abandon is true, transaction is discarded instead of committed.
*/
int xenbus_transaction_end(struct xenbus_transaction *t, int abort)
{
char abortstr[2];
int err;
if (abort)
strcpy(abortstr, "F");
else
strcpy(abortstr, "T");
err = xs_error(xs_single(t, XS_TRANSACTION_END, abortstr, NULL));
up_read(&xs_state.suspend_mutex);
return err;
}
EXPORT_SYMBOL(xenbus_transaction_end);
/* Single read and scanf: returns -errno or num scanned. */
int xenbus_scanf(struct xenbus_transaction *t,
const char *dir, const char *node, const char *fmt, ...)
{
va_list ap;
int ret;
char *val;
val = xenbus_read(t, dir, node, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
va_start(ap, fmt);
ret = vsscanf(val, fmt, ap);
va_end(ap);
kfree(val);
/* Distinctive errno. */
if (ret == 0)
return -ERANGE;
return ret;
}
EXPORT_SYMBOL(xenbus_scanf);
/* Single printf and write: returns -errno or 0. */
int xenbus_printf(struct xenbus_transaction *t,
const char *dir, const char *node, const char *fmt, ...)
{
va_list ap;
int ret;
#define PRINTF_BUFFER_SIZE 4096
char *printf_buffer;
printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
if (printf_buffer == NULL)
return -ENOMEM;
va_start(ap, fmt);
ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap);
va_end(ap);
BUG_ON(ret > PRINTF_BUFFER_SIZE-1);
ret = xenbus_write(t, dir, node, printf_buffer);
kfree(printf_buffer);
return ret;
}
EXPORT_SYMBOL(xenbus_printf);
/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
int xenbus_gather(struct xenbus_transaction *t, const char *dir, ...)
{
va_list ap;
const char *name;
int ret = 0;
va_start(ap, dir);
while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
const char *fmt = va_arg(ap, char *);
void *result = va_arg(ap, void *);
char *p;
p = xenbus_read(t, dir, name, NULL);
if (IS_ERR(p)) {
ret = PTR_ERR(p);
break;
}
if (fmt) {
if (sscanf(p, fmt, result) == 0)
ret = -EINVAL;
kfree(p);
} else
*(char **)result = p;
}
va_end(ap);
return ret;
}
EXPORT_SYMBOL(xenbus_gather);
static int xs_watch(const char *path, const char *token)
{
struct kvec iov[2];
iov[0].iov_base = (void *)path;
iov[0].iov_len = strlen(path) + 1;
iov[1].iov_base = (void *)token;
iov[1].iov_len = strlen(token) + 1;
return xs_error(xs_talkv(NULL, XS_WATCH, iov,
ARRAY_SIZE(iov), NULL));
}
static int xs_unwatch(const char *path, const char *token)
{
struct kvec iov[2];
iov[0].iov_base = (char *)path;
iov[0].iov_len = strlen(path) + 1;
iov[1].iov_base = (char *)token;
iov[1].iov_len = strlen(token) + 1;
return xs_error(xs_talkv(NULL, XS_UNWATCH, iov,
ARRAY_SIZE(iov), NULL));
}
static struct xenbus_watch *find_watch(const char *token)
{
struct xenbus_watch *i, *cmp;
cmp = (void *)simple_strtoul(token, NULL, 16);
list_for_each_entry(i, &watches, list)
if (i == cmp)
return i;
return NULL;
}
/* Register callback to watch this node. */
int register_xenbus_watch(struct xenbus_watch *watch)
{
/* Pointer in ascii is the token. */
char token[sizeof(watch) * 2 + 1];
int err;
sprintf(token, "%lX", (long)watch);
down_read(&xs_state.suspend_mutex);
spin_lock(&watches_lock);
BUG_ON(find_watch(token));
list_add(&watch->list, &watches);
spin_unlock(&watches_lock);
err = xs_watch(watch->node, token);
/* Ignore errors due to multiple registration. */
if ((err != 0) && (err != -EEXIST)) {
spin_lock(&watches_lock);
list_del(&watch->list);
spin_unlock(&watches_lock);
}
up_read(&xs_state.suspend_mutex);
return err;
}
EXPORT_SYMBOL(register_xenbus_watch);
void unregister_xenbus_watch(struct xenbus_watch *watch)
{
struct xs_stored_msg *msg, *tmp;
char token[sizeof(watch) * 2 + 1];
int err;
sprintf(token, "%lX", (long)watch);
down_read(&xs_state.suspend_mutex);
spin_lock(&watches_lock);
BUG_ON(!find_watch(token));
list_del(&watch->list);
spin_unlock(&watches_lock);
err = xs_unwatch(watch->node, token);
if (err)
printk(KERN_WARNING
"XENBUS Failed to release watch %s: %i\n",
watch->node, err);
up_read(&xs_state.suspend_mutex);
/* Cancel pending watch events. */
spin_lock(&watch_events_lock);
list_for_each_entry_safe(msg, tmp, &watch_events, list) {
if (msg->u.watch.handle != watch)
continue;
list_del(&msg->list);
kfree(msg->u.watch.vec);
kfree(msg);
}
spin_unlock(&watch_events_lock);
/* Flush any currently-executing callback, unless we are it. :-) */
if (current->pid != xenwatch_pid) {
down(&xenwatch_mutex);
up(&xenwatch_mutex);
}
}
EXPORT_SYMBOL(unregister_xenbus_watch);
void xs_suspend(void)
{
down_write(&xs_state.suspend_mutex);
down(&xs_state.request_mutex);
}
void xs_resume(void)
{
struct xenbus_watch *watch;
char token[sizeof(watch) * 2 + 1];
up(&xs_state.request_mutex);
/* No need for watches_lock: the suspend_mutex is sufficient. */
list_for_each_entry(watch, &watches, list) {
sprintf(token, "%lX", (long)watch);
xs_watch(watch->node, token);
}
up_write(&xs_state.suspend_mutex);
}
static int xenwatch_thread(void *unused)
{
struct list_head *ent;
struct xs_stored_msg *msg;
for (;;) {
wait_event_interruptible(watch_events_waitq,
!list_empty(&watch_events));
down(&xenwatch_mutex);
spin_lock(&watch_events_lock);
ent = watch_events.next;
if (ent != &watch_events)
list_del(ent);
spin_unlock(&watch_events_lock);
if (ent != &watch_events) {
msg = list_entry(ent, struct xs_stored_msg, list);
msg->u.watch.handle->callback(
msg->u.watch.handle,
(const char **)msg->u.watch.vec,
msg->u.watch.vec_size);
kfree(msg->u.watch.vec);
kfree(msg);
}
up(&xenwatch_mutex);
}
}
static int process_msg(void)
{
struct xs_stored_msg *msg;
char *body;
int err;
msg = kmalloc(sizeof(*msg), GFP_KERNEL);
if (msg == NULL)
return -ENOMEM;
err = xb_read(&msg->hdr, sizeof(msg->hdr));
if (err) {
kfree(msg);
return err;
}
body = kmalloc(msg->hdr.len + 1, GFP_KERNEL);
if (body == NULL) {
kfree(msg);
return -ENOMEM;
}
err = xb_read(body, msg->hdr.len);
if (err) {
kfree(body);
kfree(msg);
return err;
}
body[msg->hdr.len] = '\0';
if (msg->hdr.type == XS_WATCH_EVENT) {
msg->u.watch.vec = split(body, msg->hdr.len,
&msg->u.watch.vec_size);
if (IS_ERR(msg->u.watch.vec)) {
kfree(msg);
return PTR_ERR(msg->u.watch.vec);
}
spin_lock(&watches_lock);
msg->u.watch.handle = find_watch(
msg->u.watch.vec[XS_WATCH_TOKEN]);
if (msg->u.watch.handle != NULL) {
spin_lock(&watch_events_lock);
list_add_tail(&msg->list, &watch_events);
wake_up(&watch_events_waitq);
spin_unlock(&watch_events_lock);
} else {
kfree(msg->u.watch.vec);
kfree(msg);
}
spin_unlock(&watches_lock);
} else {
msg->u.reply.body = body;
spin_lock(&xs_state.reply_lock);
list_add_tail(&msg->list, &xs_state.reply_list);
spin_unlock(&xs_state.reply_lock);
wake_up(&xs_state.reply_waitq);
}
return 0;
}
static int xenbus_thread(void *unused)
{
int err;
for (;;) {
err = process_msg();
if (err)
printk(KERN_WARNING "XENBUS error %d while reading "
"message\n", err);
}
}
int xs_init(void)
{
int err;
struct task_struct *task;
INIT_LIST_HEAD(&xs_state.reply_list);
spin_lock_init(&xs_state.reply_lock);
init_waitqueue_head(&xs_state.reply_waitq);
init_MUTEX(&xs_state.request_mutex);
init_rwsem(&xs_state.suspend_mutex);
/* Initialize the shared memory rings to talk to xenstored */
err = xb_init_comms();
if (err)
return err;
task = kthread_run(xenwatch_thread, NULL, "xenwatch");
if (IS_ERR(task))
return PTR_ERR(task);
xenwatch_pid = task->pid;
task = kthread_run(xenbus_thread, NULL, "xenbus");
if (IS_ERR(task))
return PTR_ERR(task);
return 0;
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/