To manipulate shared pages from userspace, we use a simple device.
Userspace can gain access to a share by handle, mmap it, place a
watch, trigger them, and do scatter-gather transfers.
FIXME: Should use vm_insert_page these days.
diff -r 125c7cd65739 linux-2.6-xen-sparse/drivers/xen/Makefile
--- a/linux-2.6-xen-sparse/drivers/xen/Makefile Thu Jun 1 23:24:05 2006
+++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Fri Jun 2 09:25:42 2006
@@ -8,6 +8,7 @@
obj-y += balloon/
obj-y += privcmd/
obj-y += xenbus/
+obj-y += xenshare.o
obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/
obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/
diff -r 125c7cd65739 linux-2.6-xen-sparse/drivers/xen/xenshare.c
--- /dev/null Thu Jun 1 23:24:05 2006
+++ b/linux-2.6-xen-sparse/drivers/xen/xenshare.c Fri Jun 2 09:25:42 2006
@@ -0,0 +1,365 @@
+/* Userspace interface for accessing share regions.
+ *
+ * Copyright 2006 Rusty Russell <rusty@xxxxxxxxxxxxxxx> IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#define DEBUG
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <asm/hypervisor.h>
+#include <xen/interface/share.h>
+#include <xen/public/xenshare.h>
+#include <xen/evtchn.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/share.h>
+
+/* FIXME: %s/pr_debug(/pr_debug(/g*/
+struct share_info
+{
+ struct xen_share *share;
+
+ int out_sg_used;
+ unsigned int out_sg_pages;
+ struct page *out_sg;
+
+ /* Trigger they placed watch on (-1 == none) */
+ int watch_number;
+ int watch_result;
+
+ struct xen_share_handler handler;
+ wait_queue_head_t waiters;
+};
+
+/* FIXME: Should we register handlers as required? */
+static void share_io_handler(struct xen_share_handler *handler)
+{
+ struct share_info *info;
+
+ info = container_of(handler, struct share_info, handler);
+ pr_debug("xenshare: interrupt!\n");
+ wake_up_all(&info->waiters);
+}
+
+static int get_share(struct file *file, void __user *udata)
+{
+ struct xenshare_get_share share;
+ struct share_info *info;
+ int err;
+
+ if (copy_from_user(&share, udata, sizeof(share)) != 0)
+ return -EFAULT;
+
+ if (file->private_data)
+ return -EBUSY;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->out_sg = NULL;
+ info->watch_number = -1;
+ info->watch_result = 1;
+ init_waitqueue_head(&info->waiters);
+
+ info->share = xen_share_get(share.share_ref, share.num_pages);
+ if (IS_ERR(info->share)) {
+ err = PTR_ERR(info->share);
+ pr_debug("xenshare: get_share returned %i\n", err);
+ goto free_info;
+ }
+ info->handler.handler = share_io_handler;
+ xen_share_add_handler(info->share, &info->handler);
+ file->private_data = info;
+ return info->share->peerid;
+
+free_info:
+ kfree(info);
+ return err;
+}
+
+static int pages_to_sg(struct share_info *info,
+ struct xen_sg sg[],
+ unsigned long len)
+{
+ unsigned int i;
+
+ if (len > PAGE_SIZE * XEN_SG_MAX)
+ return -ENOSPC;
+
+ /* Register this length of our buffer as sg. */
+ for (i = 0; i < len/PAGE_SIZE; i++) {
+ sg[i].addr = page_to_pfn(info->out_sg + i) << PAGE_SHIFT;
+ sg[i].len = PAGE_SIZE;
+ }
+ if (len % PAGE_SIZE) {
+ sg[i].addr = page_to_pfn(info->out_sg + i) << PAGE_SHIFT;
+ sg[i].len = len % PAGE_SIZE;
+ i++;
+ }
+ return i;
+}
+
+static int send_sg(struct file *file, void __user *udata)
+{
+ struct xen_sg sg[XEN_SG_MAX];
+ struct xenshare_sg send;
+ int err;
+ struct share_info *info = file->private_data;
+
+ if (copy_from_user(&send, udata, sizeof(send)) != 0)
+ return -EFAULT;
+
+ if (!info)
+ return -EINVAL;
+
+ err = pages_to_sg(info, sg, send.len);
+ if (err >= 0)
+ err = xen_sg_xfer(info->share, send.queue, XEN_SG_OUT,
+ err, sg);
+ return err;
+}
+
+static int register_sg(struct file *file, void __user *udata)
+{
+ struct share_info *info = file->private_data;
+ struct xen_sg sg[XEN_SG_MAX];
+ struct xenshare_sg reg;
+ int err;
+
+ if (copy_from_user(®, udata, sizeof(reg)) != 0)
+ return -EFAULT;
+
+ if (!info)
+ return -EINVAL;
+
+ err = pages_to_sg(info, sg, reg.len);
+ if (err < 0)
+ return err;
+
+ info->out_sg_used = 0;
+ err = xen_sg_register(info->share, XEN_SG_IN, reg.queue,
+ &info->out_sg_used, err, sg);
+ pr_debug("xenshare: Registered sg: %i\n", err);
+ if (err)
+ info->out_sg_used = 1;
+ return err;
+}
+
+static int watch(struct file *file, unsigned long trigger)
+{
+ struct share_info *info = file->private_data;
+ int err;
+
+ if (!info)
+ return -EINVAL;
+
+ pr_debug("xenshare: watch %li\n", trigger);
+ if (info->watch_number != -1)
+ return -EBUSY;
+
+ info->watch_number = trigger;
+ err = xen_share_watch(info->share, trigger, &info->watch_result);
+ if (err)
+ info->watch_number = -1;
+ pr_debug("xenshare: watch returned %i\n", err);
+ return err;
+}
+
+static int trigger(struct file *file, unsigned long watch_number)
+{
+ struct share_info *info = file->private_data;
+
+ if (!info)
+ return -EINVAL;
+
+ pr_debug("xenshare: trigger %li\n", watch_number);
+ return xen_share_trigger(info->share, watch_number);
+}
+
+static int xenshare_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long data)
+{
+ switch (cmd) {
+ case IOCTL_XENSHARE_GET_SHARE:
+ return get_share(file, (void __user *)data);
+ case IOCTL_XENSHARE_SG_SEND:
+ return send_sg(file, (void __user *)data);
+ case IOCTL_XENSHARE_SG_REGISTER:
+ return register_sg(file, (void __user *)data);
+ case IOCTL_XENSHARE_WATCH:
+ return watch(file, data);
+ case IOCTL_XENSHARE_TRIGGER:
+ return trigger(file, data);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/* In 2.6.12, this is how you map a kernel page. Later, use vm_insert_page. */
+static struct page *share_nopage(struct vm_area_struct *vma,
+ unsigned long vaddr, int *type)
+{
+ unsigned int pagenum = (vaddr - vma->vm_start)/PAGE_SIZE;
+ if (vaddr > vma->vm_end)
+ return NOPAGE_SIGBUS;
+ if (type)
+ *type = VM_FAULT_MINOR;
+ return (struct page *)vma->vm_private_data + pagenum;
+}
+
+static struct vm_operations_struct xenshare_vm_ops =
+{
+ .nopage = share_nopage,
+};
+
+static void map_pages(struct vm_area_struct *vma, struct page *page)
+{
+ vma->vm_ops = &xenshare_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+ vma->vm_private_data = page;
+}
+
+static int create_and_map_sg(struct share_info *info,
+ struct vm_area_struct *vma)
+{
+ unsigned long pages = (vma->vm_end - vma->vm_start) / PAGE_SIZE;
+
+ if (!info->out_sg) {
+ if (pages > XEN_SG_MAX)
+ return -ENOSPC;
+ info->out_sg = alloc_pages(GFP_KERNEL, fls(pages-1));
+ if (!info->out_sg) {
+ printk("Could not allocate %i pages\n",
+ 1<<fls(pages-1));
+ return -ENOMEM;
+ }
+ info->out_sg_pages = pages;
+ /* We set this to 0 when registered with hypervisor. */
+ info->out_sg_used = 1;
+ }
+
+ /* Can't map more than we have. */
+ if (pages > info->out_sg_pages)
+ return -ENOSPC;
+
+ map_pages(vma, info->out_sg);
+ return 0;
+}
+
+static int xenshare_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct share_info *info = file->private_data;
+
+ if (!info) {
+ pr_debug("mmap before get_share file %p\n", file);
+ return -EINVAL;
+ }
+
+ if (vma->vm_pgoff == 0)
+ return create_and_map_sg(info, vma);
+ if (vma->vm_pgoff == XENSHARE_MAP_SHARE_PAGE)
+ return xen_share_map(info->share, vma);
+ pr_debug("Unknown mmap offset %li\n", vma->vm_pgoff);
+ return -EINVAL;
+}
+
+/* Read means wait for sg to be used / watch to be fired. */
+static ssize_t read(struct file *file, char __user *ubuf, size_t size,
+ loff_t *off)
+{
+ int err;
+ struct share_info *info = file->private_data;
+
+ if (!info)
+ return -EINVAL;
+ if (size != sizeof(info->out_sg_used))
+ return -EINVAL;
+
+ err = wait_event_interruptible(info->waiters,
+ info->out_sg_used ||
!info->watch_result);
+ if (err)
+ return err;
+
+ /* 0 or negative indicates the watch fired. */
+ if (info->watch_result <= 0) {
+ int watch = -info->watch_number;
+ info->watch_result = 1;
+ pr_debug("Watch number %i\n", info->watch_number);
+ if (copy_to_user(ubuf, &watch, 4) != 0)
+ return -EFAULT;
+ } else {
+ pr_debug("sg_used %i\n", info->out_sg_used);
+ if (copy_to_user(ubuf, &info->out_sg_used, 4) != 0)
+ return -EFAULT;
+ }
+ return size;
+}
+
+/* Free up allocated evtchn port and drop share */
+static int xenshare_release(struct inode *inode, struct file *file)
+{
+ struct share_info *info = file->private_data;
+
+ /* If private_data isn't allocated we we're opened and closed
+ * without doing anything interesting */
+ if (!info)
+ return 0;
+
+ /* Unregister sg before drop */
+ if (info->out_sg)
+ xen_sg_unregister(info->share,
+ page_to_pfn(info->out_sg) << PAGE_SHIFT);
+
+ if (info->watch_number != -1)
+ xen_share_unwatch(info->share, info->watch_number);
+
+ xen_share_remove_handler(info->share, &info->handler);
+ xen_share_put(info->share);
+ kfree(info);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static struct file_operations xenshare_file_ops = {
+ .ioctl = xenshare_ioctl,
+ .mmap = xenshare_mmap,
+ .read = read,
+ .release = xenshare_release,
+};
+
+static int init(void)
+{
+ struct class *xen_class;
+ int err;
+
+ err = register_chrdev(0, "xenshare", &xenshare_file_ops);
+ if (err < 0)
+ return err;
+
+ xen_class = class_create(THIS_MODULE, "xen");
+ /* FIXME: save struct class_device * */
+ (void*)class_device_create(xen_class, NULL, MKDEV(err, 0), NULL,
"xenshare");
+ return 0;
+}
+module_init(init);
+MODULE_LICENSE("GPL");
diff -r 125c7cd65739 linux-2.6-xen-sparse/include/xen/public/xenshare.h
--- /dev/null Thu Jun 1 23:24:05 2006
+++ b/linux-2.6-xen-sparse/include/xen/public/xenshare.h Fri Jun 2
09:25:42 2006
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * xenshare.h
+ *
+ * Interface to /dev/xenshare.
+ *
+ * Copyright 2006 Rusty Russell <rusty@xxxxxxxxxxxxxxx> 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 __LINUX_XENSHARE_H__
+#define __LINUX_XENSHARE_H__
+
+/* Device is used as follows:
+ * (1) IOCTL_XENSHARE_GET_SHARE is called to get the share. Then you can
+ * mmap at page XENSHARE_MAP_SHARE_PAGE to access it.
+ * (2) mmap at 0 creates a scatter-gather list.
+ * (3) Writing a 4-byte length to the fd registers it with the share.
+ * (4) Reading the fd blocks until the sg is filled. The 4-byte
+ * length is returned.
+ * (5) IOCTL_XENSHARE_SG_SEND is called to send an sg.
+ */
+
+#define XENSHARE_MAP_SHARE_PAGE 0x1000
+
+struct xenshare_get_share
+{
+ unsigned long share_ref;
+ unsigned int num_pages;
+};
+
+struct xenshare_sg
+{
+ unsigned long len;
+ uint32_t queue;
+};
+
+/* After this, you can mmap XENSHARE_MAP_PAGE to access share.
+ * Returns peerid. */
+#define IOCTL_XENSHARE_GET_SHARE \
+ _IOR('P', 100, struct xenshare_get_share)
+
+/* Transfers the xenshare_sg. */
+#define IOCTL_XENSHARE_SG_SEND \
+ _IOR('P', 101, struct xenshare_sg)
+
+/* Registers the xenshare_sg */
+#define IOCTL_XENSHARE_SG_REGISTER \
+ _IOR('P', 102, struct xenshare_sg)
+
+/* Registers a watch (currently only 1): read returns -triggernum */
+#define IOCTL_XENSHARE_WATCH \
+ _IO('P', 103)
+
+/* Triggers a watch: returns same a hypercall */
+#define IOCTL_XENSHARE_TRIGGER \
+ _IO('P', 104)
+
+#endif /* __LINUX_XENSHARE_H__ */
--
ccontrol: http://ccontrol.ozlabs.org
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|