On the Linux kernel side, we provide some wrappers for accessing
shared pages. They are currently reference-counted, because a future
patch allows userspace to access shared pages, and the Xen interface
will refuse the second request for access by the same domain.
The entire hypercall interface is arch-wrapped, which is probably
overkill, but I wasn't entirely sure of the needs of non-x86
architectures. Some of this should almost certainly be in common code.
diff -r 6d476981e3a5 -r 07a00d96357d
linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/share.h
--- /dev/null Sun May 28 14:49:17 2006
+++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/share.h Wed May
31 05:33:38 2006
@@ -0,0 +1,62 @@
+#ifndef __ASM_XEN_I386_SHARE_H
+#define __ASM_XEN_I386_SHARE_H
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <xen/interface/share.h>
+
+struct xen_share
+{
+ struct list_head list;
+ atomic_t use;
+ share_ref_t share_ref;
+ unsigned num_pages;
+ void *addr;
+ int event_channel;
+ int peerid;
+ int irq;
+ struct list_head handlers;
+};
+
+struct xen_share_handler
+{
+ struct list_head list;
+ void (*handler)(struct xen_share_handler *h);
+};
+
+/* Map a shared area. Returns PTR_ERR(errno) on fail. */
+struct xen_share *xen_share_get(share_ref_t share_ref, unsigned pages);
+
+/* Set up handler for events. */
+void xen_share_add_handler(struct xen_share *s, struct xen_share_handler *h);
+
+/* Remove handler. */
+void xen_share_remove_handler(struct xen_share *s,
+ struct xen_share_handler *h);
+
+/* Unmap a shared area (irq unbound if not done already). */
+void xen_share_put(struct xen_share *share);
+
+/* Register this sg list (physical kernel addresses). Returns 0 on success. */
+int xen_sg_register(struct xen_share *share, int dirmask, u32 queue, u32 *lenp,
+ unsigned int num_sgs, const struct xen_sg sg[]);
+
+/* Unregister this sg list: give first phys address of sg. */
+void xen_sg_unregister(struct xen_share *share, unsigned long sgaddr);
+
+/* Transfer this sg list (physical kernel addresses). Returns len xferred. */
+int xen_sg_xfer(struct xen_share *share, u32 queue, int dir,
+ unsigned int num_sgs, const struct xen_sg sg[]);
+
+/* Place watch on this trigger. Returns 0 on success. */
+int xen_share_watch(struct xen_share *share, int triggernum, u32 *resultp);
+
+/* Remove watch on this trigger. */
+void xen_share_unwatch(struct xen_share *share, int triggernum);
+
+/* Trigger a watch. Returns num watching on success. */
+int xen_share_trigger(struct xen_share *share, int triggernum);
+
+/* Map a share into a vma (for userspace mmap). */
+int xen_share_map(struct xen_share *share, struct vm_area_struct *vma);
+#endif /* __ASM_XEN_I386_SHARE_H */
diff -r 6d476981e3a5 -r 07a00d96357d
linux-2.6-xen-sparse/arch/i386/kernel/share-xen.c
--- /dev/null Sun May 28 14:49:17 2006
+++ b/linux-2.6-xen-sparse/arch/i386/kernel/share-xen.c Wed May 31 05:33:38 2006
@@ -0,0 +1,280 @@
+/* x86 layer for share hypercalls.
+ * 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
+ */
+#include <linux/sched.h>
+#include <linux/page-flags.h>
+#include <linux/vmalloc.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <asm/share.h>
+#include <asm/io.h>
+#include <xen/evtchn.h>
+#include <asm/hypervisor.h>
+
+/* We only request each area from the hypervisor once, so track them. */
+static DECLARE_MUTEX(share_lock);
+static spinlock_t handler_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(shares);
+
+static int get_evtchn_port(void)
+{
+ int err;
+ struct evtchn_alloc_unbound evtchn = { .dom = DOMID_SELF,
+ .remote_dom = DOMID_SELF };
+
+ err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &evtchn);
+ if (err)
+ return err;
+
+ return evtchn.port;
+}
+
+static void close_evtchn_port(int port)
+{
+ struct evtchn_close evtchn;
+ evtchn.port = port;
+ BUG_ON(HYPERVISOR_event_channel_op(EVTCHNOP_close, &evtchn) != 0);
+}
+
+static struct xen_share *get_share(share_ref_t share_ref)
+{
+ struct xen_share *i;
+
+ list_for_each_entry(i, &shares, list) {
+ if (i->share_ref == share_ref) {
+ atomic_inc(&i->use);
+ return i;
+ }
+ }
+ return NULL;
+}
+
+static irqreturn_t share_irq(int irq, void *share_, struct pt_regs *regs)
+{
+ struct xen_share *share = share_;
+ struct xen_share_handler *h;
+
+ list_for_each_entry(h, &share->handlers, list)
+ h->handler(h);
+ return IRQ_HANDLED;
+}
+
+struct xen_share *create_share(share_ref_t share_ref, unsigned pages)
+{
+ pgprot_t prot;
+ int err;
+ struct vm_struct *vma;
+ struct xen_share *share;
+
+ share = kmalloc(sizeof(struct xen_share), GFP_KERNEL);
+ if (!share) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ share->share_ref = share_ref;
+ share->num_pages = pages;
+ atomic_set(&share->use, 1);
+ INIT_LIST_HEAD(&share->handlers);
+ vma = get_vm_area(pages * PAGE_SIZE, VM_IOREMAP);
+ if (!vma) {
+ err = -ENOMEM;
+ goto free_share;
+ }
+
+ share->event_channel = get_evtchn_port();
+ if (share->event_channel < 0) {
+ err = share->event_channel;
+ goto free_vma;
+ }
+
+ err = bind_evtchn_to_irqhandler(share->event_channel, share_irq,
+ SA_SHIRQ, "xenshare", share);
+ if (err < 0)
+ goto close_evtchn;
+ share->irq = err;
+
+ share->peerid = HYPERVISOR_share(XEN_SHARE_get, share_ref,
+ share->event_channel, 0, 0);
+ if (share->peerid < 0) {
+ err = share->peerid;
+ goto unbind_evtchn;
+ }
+
+ prot = __pgprot(_PAGE_PRESENT|_PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED);
+ err = direct_kernel_remap_pfn_range((unsigned long)vma->addr,
+ share_ref, pages * PAGE_SIZE,
+ prot, DOMID_SELF);
+ if (err)
+ goto put_share;
+ share->addr = vma->addr;
+ list_add(&share->list, &shares);
+
+ return share;
+
+put_share:
+ BUG_ON(HYPERVISOR_share(XEN_SHARE_drop,share->share_ref,0,0,0) != 0);
+unbind_evtchn:
+ unbind_from_irqhandler(share->irq, share);
+ goto free_vma;
+close_evtchn:
+ close_evtchn_port(share->event_channel);
+free_vma:
+ kfree(vma);
+free_share:
+ kfree(share);
+fail:
+ return ERR_PTR(err);
+}
+
+/* Map a shared area. Returns PTR_ERR(errno) on fail. */
+struct xen_share *xen_share_get(share_ref_t share_ref, unsigned pages)
+{
+ struct xen_share *share;
+
+ down(&share_lock);
+ share = get_share(share_ref);
+ if (share)
+ BUG_ON(share->num_pages != pages);
+ else
+ share = create_share(share_ref, pages);
+ up(&share_lock);
+
+ return share;
+}
+
+void xen_share_add_handler(struct xen_share *s, struct xen_share_handler *h)
+{
+ spin_lock_irq(&handler_lock);
+ list_add(&h->list, &s->handlers);
+ spin_unlock_irq(&handler_lock);
+}
+
+/* Remove irq handler. */
+void xen_share_remove_handler(struct xen_share *s, struct xen_share_handler *h)
+{
+ BUG_ON(list_empty(&s->handlers));
+ spin_lock_irq(&handler_lock);
+ list_del(&h->list);
+ spin_unlock_irq(&handler_lock);
+}
+
+/* Unmap a shared area. */
+void xen_share_put(struct xen_share *share)
+{
+ down(&share_lock);
+ if (atomic_dec_and_test(&share->use)) {
+ BUG_ON(!list_empty(&share->handlers));
+ unbind_from_irqhandler(share->irq, share);
+
+ /* This also kfrees vma. */
+ vunmap(share->addr);
+ BUG_ON(HYPERVISOR_share(XEN_SHARE_drop, share->share_ref, 0,
+ 0, 0) != 0);
+ list_del(&share->list);
+ kfree(share);
+ }
+ up(&share_lock);
+}
+
+/* Register this sg list (physical kernel addresses). Returns 0 on success. */
+int xen_sg_register(struct xen_share *s, int dirmask, u32 queue, u32 *lenp,
+ unsigned int num_sgs, const struct xen_sg sg[])
+{
+ struct xen_sg new_sg[XEN_SG_MAX];
+ unsigned int i;
+
+ /* We feed machine addresses to hypervisor. */
+ for (i = 0; i < num_sgs; i++) {
+ new_sg[i].addr = phys_to_machine(sg[i].addr);
+ new_sg[i].len = sg[i].len;
+ }
+
+ return HYPERVISOR_share(XEN_SHARE_sg_register, s->share_ref,
+ xen_share_sg_arg(queue, num_sgs, dirmask),
+ (long)new_sg,
+ virt_to_machine(lenp));
+}
+
+/* Unregister this sg list. */
+void xen_sg_unregister(struct xen_share *s, unsigned long addr)
+{
+ BUG_ON(HYPERVISOR_share(XEN_SHARE_sg_unregister, s->share_ref,
+ phys_to_machine(addr), 0, 0) != 0);
+}
+
+/* Transfer this sg list (physical kernel addresses). Returns len xferred. */
+int xen_sg_xfer(struct xen_share *s, u32 queue, int dir,
+ unsigned int num_sgs, const struct xen_sg sg[])
+{
+ struct xen_sg new_sg[XEN_SG_MAX];
+ unsigned int i;
+
+ /* Hypervisor wants virtual addresses here. */
+ for (i = 0; i < num_sgs; i++) {
+ new_sg[i].addr = (long)phys_to_virt(sg[i].addr);
+ new_sg[i].len = sg[i].len;
+ }
+
+ return HYPERVISOR_share(XEN_SHARE_sg_xfer, s->share_ref,
+ xen_share_sg_arg(queue, num_sgs, dir),
+ (long)new_sg, 0);
+}
+
+/* Place watch on this trigger. Returns 0 on success. */
+int xen_share_watch(struct xen_share *s, int triggernum, u32 *resultp)
+{
+ return HYPERVISOR_share(XEN_SHARE_watch, s->share_ref, triggernum,
+ virt_to_machine(resultp), 0);
+}
+
+/* Remove watch on this trigger. */
+void xen_share_unwatch(struct xen_share *s, int triggernum)
+{
+ BUG_ON(HYPERVISOR_share(XEN_SHARE_unwatch, s->share_ref, triggernum,
+ 0, 0) != 0);
+}
+
+/* Trigger a watch. Returns num watching on success. */
+int xen_share_trigger(struct xen_share *s, int trigger)
+{
+ return HYPERVISOR_share(XEN_SHARE_trigger, s->share_ref, trigger,0,0);
+}
+
+int xen_share_map(struct xen_share *s, struct vm_area_struct *vma)
+{
+ vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
+ return direct_remap_pfn_range(vma, vma->vm_start,
+ s->share_ref,
+ s->num_pages * PAGE_SIZE,
+ vma->vm_page_prot, DOMID_SELF);
+}
+
+EXPORT_SYMBOL_GPL(xen_share_get);
+EXPORT_SYMBOL_GPL(xen_share_put);
+EXPORT_SYMBOL_GPL(xen_share_map);
+EXPORT_SYMBOL_GPL(xen_share_trigger);
+EXPORT_SYMBOL_GPL(xen_share_watch);
+EXPORT_SYMBOL_GPL(xen_share_unwatch);
+EXPORT_SYMBOL_GPL(xen_sg_xfer);
+EXPORT_SYMBOL_GPL(xen_sg_register);
+EXPORT_SYMBOL_GPL(xen_sg_unregister);
+EXPORT_SYMBOL_GPL(xen_share_add_handler);
+EXPORT_SYMBOL_GPL(xen_share_remove_handler);
diff -r 6d476981e3a5 -r 07a00d96357d
linux-2.6-xen-sparse/arch/i386/kernel/Makefile
--- a/linux-2.6-xen-sparse/arch/i386/kernel/Makefile Sun May 28 14:49:17 2006
+++ b/linux-2.6-xen-sparse/arch/i386/kernel/Makefile Wed May 31 05:33:38 2006
@@ -88,6 +88,7 @@
include $(srctree)/scripts/Makefile.xen
obj-y += fixup.o
+obj-y += share-xen.o
microcode-$(subst m,y,$(CONFIG_MICROCODE)) := microcode-xen.o
n-obj-xen := i8259.o timers/ reboot.o smpboot.o trampoline.o
diff -r 6d476981e3a5 -r 07a00d96357d
linux-2.6-xen-sparse/arch/i386/mm/ioremap-xen.c
--- a/linux-2.6-xen-sparse/arch/i386/mm/ioremap-xen.c Sun May 28 14:49:17 2006
+++ b/linux-2.6-xen-sparse/arch/i386/mm/ioremap-xen.c Wed May 31 05:33:38 2006
@@ -123,8 +123,11 @@
/* Same as remap_pfn_range(). */
vma->vm_flags |= VM_IO | VM_RESERVED;
+ /* FIXME: xenshare needs to pass DOMID_SELF. Check it's save to remove
+ * the check.
if (domid == DOMID_SELF)
return -EINVAL;
+ */
return __direct_remap_pfn_range(
vma->vm_mm, address, mfn, size, prot, domid);
diff -r 6d476981e3a5 -r 07a00d96357d
linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h
--- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h Sun May
28 14:49:17 2006
+++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h Wed May
31 05:33:38 2006
@@ -359,5 +359,11 @@
return _hypercall2(int, xenoprof_op, op, arg);
}
+static inline long
+HYPERVISOR_share(
+ int op, long arg1, long arg2, long arg3, long arg4)
+{
+ return _hypercall5(long, share_op, op, arg1, arg2, arg3, arg4);
+}
#endif /* __HYPERCALL_H__ */
diff -r 6d476981e3a5 -r 07a00d96357d patches/linux-2.6.12/get_vm_area.patch
--- /dev/null Sun May 28 14:49:17 2006
+++ b/patches/linux-2.6.12/get_vm_area.patch Wed May 31 05:33:38 2006
@@ -0,0 +1,9 @@
+diff -Naur linux-2.6.12/mm/vmalloc.c linux-2.6.12.post/mm/vmalloc.c
+--- linux-2.6.12/mm/vmalloc.c 2005-06-18 05:48:29.000000000 +1000
++++ linux-2.6.12.post/mm/vmalloc.c 2006-01-10 16:56:36.000000000 +1100
+@@ -247,6 +247,7 @@
+ {
+ return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END);
+ }
++EXPORT_SYMBOL(get_vm_area);
+
--
ccontrol: http://ccontrol.ozlabs.org
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|