From: Jean Guyader Date: Wed, 4 Nov 2009 05:27:08 +0000 (+0000) Subject: Create a char device to handler v2v connection in userland. X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=4affbc94e1327388134cab8b2c62e4a9a4b92cdf;p=xenclient%2Flinux-2.6.27-pq.git Create a char device to handler v2v connection in userland. --- diff --git a/master/series b/master/series index 33a4d80..fe0cb9d 100644 --- a/master/series +++ b/master/series @@ -356,3 +356,4 @@ itpm v2v-core v2v-async v2v-accept-nonblock +v2v-dev diff --git a/master/v2v-dev b/master/v2v-dev new file mode 100644 index 0000000..5b4a425 --- /dev/null +++ b/master/v2v-dev @@ -0,0 +1,432 @@ +diff --git a/drivers/xen/v2v/Kconfig b/drivers/xen/v2v/Kconfig +index 5966234..b3b6ef4 100644 +--- a/drivers/xen/v2v/Kconfig ++++ b/drivers/xen/v2v/Kconfig +@@ -22,3 +22,10 @@ config XEN_V2V_DRV + default n + help + Sample for Xen V2V interdomain communication services. ++ ++config XEN_V2V_DEV ++ tristate "Xen V2V device" ++ depends on XEN_V2V ++ default m ++ help ++ Xen V2V char device for userland v2v. +diff --git a/drivers/xen/v2v/Makefile b/drivers/xen/v2v/Makefile +index f3442d9..5230cd6 100644 +--- a/drivers/xen/v2v/Makefile ++++ b/drivers/xen/v2v/Makefile +@@ -1,5 +1,6 @@ + + obj-$(CONFIG_XEN_V2V) += v2v.o v2vutl.o + obj-$(CONFIG_XEN_V2V_DRV) += v2vdrv.o v2vops.o ++obj-$(CONFIG_XEN_V2V_DEV) += v2vdev.o + + ccflags-$(CONFIG_XEN_V2V_DEBUG) += -DDEBUG +diff --git a/drivers/xen/v2v/v2vdev.c b/drivers/xen/v2v/v2vdev.c +new file mode 100644 +index 0000000..c7afdf3 +--- /dev/null ++++ b/drivers/xen/v2v/v2vdev.c +@@ -0,0 +1,400 @@ ++/****************************************************************************** ++ * drivers/xen/v2v/v2vdev.c ++ * ++ * V2V broker. ++ * ++ * Copyright (c) 2009 Jean Guyader ++ * Copyright (c) 2009 Citrix Systems, Inc. ++ * ++ * 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 filp (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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define V2VLISTEN(len) _IOC(_IOC_WRITE, 'V', 0x01, len) ++#define V2VCONNECT(len) _IOC(_IOC_WRITE, 'V', 0x02, len) ++#define V2VTYPE _IOC(_IOC_WRITE, 'V', 0x03, int) ++ ++#define MIN(_x_, _y_) ((_x_) > (_y_) ? (_x_) : (_y_)) ++ ++#define V2VDEV_LOG "V2V-dev" ++ ++struct v2vdev_context ++{ ++ int connected; ++ void *buff; ++ size_t buff_size; ++ int last_err; ++ int listenner; ++ int type; ++ struct v2v_channel *channel; ++}; ++ ++ ++static void ++hexdump (char *prefix, void *_d, int len) ++{ ++ uint8_t *d = (uint8_t *) _d; ++ int i, j, k; ++ int s, e; ++ ++ printk (KERN_ERR "%s %d bytes from %p\n", prefix, len, d); ++ ++ if (!d || len<0) ++ return; ++ ++ e = len + 15; ++ e &= ~15; ++ ++ for (i = 0; i < e; i += 16) ++ { ++ printk (KERN_ERR "%s %05x:", prefix, i); ++ for (j = 0; j < 16; ++j) ++ { ++ k = i + j; ++ ++ if (k < len) ++ printk (" %02x", d[k]); ++ else ++ printk (" "); ++ if (j == 7) ++ printk (" "); ++ } ++ ++ for (j = 0; j < 16; ++j) ++ { ++ k = i + j; ++ if (k < len) ++ { ++ uint8_t c = d[k]; ++ if (c < 33) ++ c = '.'; ++ if (c > 126) ++ c = '.'; ++ printk ("%c", c); ++ } ++ else ++ printk (" "); ++ if (j == 7) ++ printk (" "); ++ } ++ printk ("\n"); ++ } ++} ++ ++static int v2vdev_wait_connected(struct v2vdev_context *ctx) ++{ ++ struct v2v_wait *wait_state; ++ u8 reasons_mask = V2V_WAKE_REASON_CONTROL; ++ int err; ++ enum v2v_endpoint_state state; ++ ++ while (!ctx->connected) ++ { ++ wait_state = v2v_get_wait_state(ctx->channel); ++ printk(V2VDEV_LOG" wait event ....\n"); ++ wait_event(wait_state->wait_event, ++ atomic_xchg(&wait_state->wait_condition, 0) == 1); ++ v2v_get_wake_reason(ctx->channel, reasons_mask); ++ ++ err = v2v_get_remote_state(ctx->channel, &state); ++ if (err) ++ { ++ printk(V2VDEV_LOG" failure in v2v_get_remote_state() - err: %d\n", err); ++ v2v_disconnect(ctx->channel); ++ return err; ++ } ++ printk(V2VDEV_LOG" endpoint state: %s\n", v2v_endpoint_state_name(state)); ++ ctx->connected = state == v2v_state_connected; ++ } ++ return 0; ++} ++ ++static int v2vdev_listen(struct v2vdev_context *ctx, ++ const char __user *user_path, size_t size) ++{ ++ int err; ++ char path[1024]; ++ ++ if (copy_from_user(path, user_path, size) != 0) ++ return -EINVAL; ++ path[size] = 0; ++ ++ err = v2v_listen(path, &ctx->channel, 0, 0, 0); ++ if (err) ++ { ++ printk(V2VDEV_LOG" failure in v2v_listen() - error: %d\n", err); ++ return err; ++ } ++ BUG_ON(ctx->channel == NULL); ++ ++ ctx->listenner = 1; ++ ++ return 0; ++} ++ ++static int v2vdev_connect(struct v2vdev_context *ctx, ++ const char __user *user_path, size_t size) ++{ ++ char path[1024]; ++ int err; ++ ++ if (copy_from_user(path, user_path, size) != 0) ++ return -EINVAL; ++ path[size] = 0; ++ ++ printk(V2VDEV_LOG" connect on %s\n", path); ++ ++ err = v2v_connect(path, &ctx->channel, 0); ++ if (err) ++ { ++ printk(V2VDEV_LOG" failure in connect() - err: %d\n", err); ++ return err; ++ } ++ ++ err = v2vdev_wait_connected(ctx); ++ if (err) ++ { ++ printk(V2VDEV_LOG" failure in wait_connected() - error: %d\n", err); ++ return err; ++ } ++ return 0; ++} ++ ++static ssize_t v2vdev_read(struct file *filp, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct v2vdev_context *ctx = filp->private_data; ++ ssize_t n, size; ++ uint16_t tag; ++ ++ if (ctx->last_err) ++ return -ctx->last_err; ++ ++ size = MIN(count, ctx->buff_size); ++ n = copy_to_user(buf, ctx->buff, size); ++ ctx->buff_size -= size; ++ if (ctx->buff_size == 0) ++ { ++ kfree(ctx->buff); ++ ctx->buff = NULL; ++ } ++ else ++ ctx->buff += size; ++ ++ v2v_nc2_finish_message(ctx->channel); ++ v2v_set_wake_reason(ctx->channel, V2V_WAKE_REASON_RECEIVE); ++ return size - n; ++} ++ ++static ssize_t v2vdev_write(struct file *filp, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct v2vdev_context *ctx = filp->private_data; ++ volatile void *msg; ++ int err; ++ ++ err = v2v_nc2_prep_message(ctx->channel, count, ctx->type, 0, &msg); ++ if (err) ++ { ++ printk(V2VDEV_LOG" failure in v2v_nc2_prep_message() - error: %d\n", err); ++ return -EAGAIN; ++ } ++ ++ if (copy_from_user((void *)msg, buf, count) != 0) ++ { ++ printk(V2VDEV_LOG" failure in copy_from_user()\n"); ++ return 0; ++ } ++ ++ hexdump(">", msg, count); ++ v2v_nc2_send_messages(ctx->channel); ++ v2v_set_wake_reason(ctx->channel, V2V_WAKE_REASON_SEND); ++ return count; ++} ++ ++static long v2vdev_ioctl(struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ int rc; ++ struct v2vdev_context *ctx = filp->private_data; ++ ++ rc = -ENOSYS; ++ switch (cmd) { ++ default: ++ if (_IOC_TYPE(cmd) != 'V') ++ return -EINVAL; ++ ++ if (_IOC_DIR(cmd) == _IOC_WRITE) ++ { ++ if ((_IOC_NR(cmd) == _IOC_NR(V2VLISTEN(0)))) ++ rc = v2vdev_listen(ctx, (void __user *)arg, _IOC_SIZE(cmd)); ++ if ((_IOC_NR(cmd) == _IOC_NR(V2VCONNECT(0)))) ++ rc = v2vdev_connect(ctx, (void __user *)arg, _IOC_SIZE(cmd)); ++ if ((_IOC_NR(cmd) == _IOC_NR(V2VTYPE))) ++ ctx->type = arg; ++ } ++ break; ++ } ++ return rc; ++} ++ ++static int v2vdev_open(struct inode *inode, struct file *filp) ++{ ++ struct v2vdev_context *ctx; ++ ++ ctx = kmalloc(sizeof (struct v2vdev_context), GFP_KERNEL); ++ memset(ctx, 0, sizeof (struct v2vdev_context)); ++ /* Set the default type to DATA_STREAM */ ++ ctx->type = 1; ++ filp->private_data = ctx; ++ return 0; ++} ++ ++static int v2vdev_release(struct inode *inode, struct file *filp) ++{ ++ struct v2vdev_context *ctx = filp->private_data; ++ ++ if (ctx->connected) ++ v2v_disconnect(ctx->channel); ++ if (ctx->buff) ++ kfree(ctx->buff); ++ kfree(filp->private_data); ++ return 0; ++} ++ ++static int v2vdev_message_get(struct v2vdev_context *ctx) ++{ ++ volatile void *buff; ++ ssize_t size = 0; ++ unsigned vtype, vflags; ++ uint16_t tag; ++ ++ vtype = vflags = 0; ++ ctx->last_err = v2v_nc2_get_message(ctx->channel, ++ (const volatile void **)&buff, &size, ++ &vtype, &vflags); ++ if (ctx->last_err == 0) ++ { ++ hexdump("<", buff, size); ++ ctx->buff = krealloc(ctx->buff, ctx->buff_size + size, GFP_KERNEL); ++ memcpy(ctx->buff + ctx->buff_size, (void *)buff, size); ++ ctx->buff_size += size; ++ } ++ return ctx->last_err; ++} ++ ++static unsigned int v2vdev_poll(struct file *filp, poll_table *wait) ++{ ++ unsigned int mask = 0; ++ struct v2vdev_context *ctx = filp->private_data; ++ struct v2v_wait *wait_state; ++ u8 reasons_mask = V2V_WAKE_REASON_CONTROL; ++ u8 reason; ++ ++ if (!ctx->connected) ++ reasons_mask |= V2V_WAKE_REASON_ANY; ++ else ++ reasons_mask |= V2V_WAKE_REASON_RECEIVE; ++ ++ wait_state = v2v_get_wait_state(ctx->channel); ++ poll_wait(filp, &wait_state->wait_event, wait); ++ ++ if (!ctx->connected && ctx->listenner) ++ if (v2v_accept(ctx->channel, 1) != EAGAIN) ++ { ++ v2vdev_wait_connected(ctx); ++ printk(V2VDEV_LOG" connected !\n"); ++ return 0; ++ } ++ ++ reason = v2v_get_wake_reason(ctx->channel, reasons_mask); ++ if (reason & V2V_WAKE_REASON_RECEIVE) ++ if (v2vdev_message_get(ctx) == 0) ++ mask = POLLIN | POLLRDNORM; ++ return mask; ++} ++ ++static const struct file_operations v2vdev_fops = { ++ .owner = THIS_MODULE, ++ .write = v2vdev_write, ++ .read = v2vdev_read, ++ .unlocked_ioctl = v2vdev_ioctl, ++ .open = v2vdev_open, ++ .release = v2vdev_release, ++ .poll = v2vdev_poll, ++}; ++ ++static struct miscdevice v2vdev_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "v2v", ++ .fops = &v2vdev_fops, ++}; ++ ++static int __init v2vdev_init(void) ++{ ++ int err = 0; ++ ++ if (!is_running_on_xen()) ++ return -ENODEV; ++ ++ err = misc_register(&v2vdev_miscdev); ++ if (err != 0) { ++ printk(KERN_ALERT "Could not register /dev/v2v\n"); ++ return err; ++ } ++ ++ printk("Xen V2V device installed.\n"); ++ ++ return 0; ++} ++ ++static void __exit v2vdev_cleanup(void) ++{ ++ misc_deregister(&v2vdev_miscdev); ++} ++ ++module_init(v2vdev_init); ++module_exit(v2vdev_cleanup); ++ ++MODULE_LICENSE("Dual BSD/GPL");