Import xenbus sources, from linux sparse tree in the 20060107 xen-3.0
snapshot.
This commit is contained in:
parent
df06d5bae0
commit
ed3fe49dd5
8
sys/arch/xen/xenbus/Makefile
Normal file
8
sys/arch/xen/xenbus/Makefile
Normal 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
|
256
sys/arch/xen/xenbus/xenbus_client.c
Normal file
256
sys/arch/xen/xenbus/xenbus_client.c
Normal 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",
|
||||
¤t_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:
|
||||
*/
|
203
sys/arch/xen/xenbus/xenbus_comms.c
Normal file
203
sys/arch/xen/xenbus/xenbus_comms.c
Normal 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:
|
||||
*/
|
50
sys/arch/xen/xenbus/xenbus_comms.h
Normal file
50
sys/arch/xen/xenbus/xenbus_comms.h
Normal 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:
|
||||
*/
|
241
sys/arch/xen/xenbus/xenbus_dev.c
Normal file
241
sys/arch/xen/xenbus/xenbus_dev.c
Normal 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:
|
||||
*/
|
1026
sys/arch/xen/xenbus/xenbus_probe.c
Normal file
1026
sys/arch/xen/xenbus/xenbus_probe.c
Normal file
File diff suppressed because it is too large
Load Diff
827
sys/arch/xen/xenbus/xenbus_xs.c
Normal file
827
sys/arch/xen/xenbus/xenbus_xs.c
Normal 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:
|
||||
*/
|
Loading…
Reference in New Issue
Block a user