diff options
author | kaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk> | 2004-02-29 15:51:23 +0000 |
---|---|---|
committer | kaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk> | 2004-02-29 15:51:23 +0000 |
commit | 5fe26fde8cc56c4520cd72644d5af9b51553baf4 (patch) | |
tree | 49d5ab9cd62470893c5eac5c4b426c9c1bcd0764 /xenolinux-2.4.25-sparse/arch | |
parent | 852c5cea72e07706be1e778f6cdd03c8026ee966 (diff) | |
download | xen-5fe26fde8cc56c4520cd72644d5af9b51553baf4.tar.gz xen-5fe26fde8cc56c4520cd72644d5af9b51553baf4.tar.bz2 xen-5fe26fde8cc56c4520cd72644d5af9b51553baf4.zip |
bitkeeper revision 1.756 (40420a7bDsT_OI1_rz4fkewUJosvRA)
xl_evtchn.c, Makefile:
new file
hypervisor.c, network.c, Makefile:
Event-channel demuxing in Xenolinux.
Diffstat (limited to 'xenolinux-2.4.25-sparse/arch')
5 files changed, 410 insertions, 4 deletions
diff --git a/xenolinux-2.4.25-sparse/arch/xeno/Makefile b/xenolinux-2.4.25-sparse/arch/xeno/Makefile index 815c28107b..710caaddbe 100644 --- a/xenolinux-2.4.25-sparse/arch/xeno/Makefile +++ b/xenolinux-2.4.25-sparse/arch/xeno/Makefile @@ -48,7 +48,8 @@ HEAD := arch/xeno/kernel/head.o arch/xeno/kernel/init_task.o SUBDIRS += arch/xeno/kernel arch/xeno/mm arch/xeno/lib SUBDIRS += arch/xeno/drivers/console arch/xeno/drivers/network -SUBDIRS += arch/xeno/drivers/block arch/xeno/drivers/balloon +SUBDIRS += arch/xeno/drivers/evtchn arch/xeno/drivers/block +SUBDIRS += arch/xeno/drivers/balloon ifdef CONFIG_XENO_PRIV SUBDIRS += arch/xeno/drivers/dom0 endif @@ -56,6 +57,7 @@ endif CORE_FILES += arch/xeno/kernel/kernel.o arch/xeno/mm/mm.o CORE_FILES += arch/xeno/drivers/console/con.o CORE_FILES += arch/xeno/drivers/block/blk.o +CORE_FILES += arch/xeno/drivers/evtchn/evtchn.o CORE_FILES += arch/xeno/drivers/network/net.o ifdef CONFIG_XENO_PRIV CORE_FILES += arch/xeno/drivers/dom0/dom0.o diff --git a/xenolinux-2.4.25-sparse/arch/xeno/drivers/evtchn/Makefile b/xenolinux-2.4.25-sparse/arch/xeno/drivers/evtchn/Makefile new file mode 100644 index 0000000000..8384c8658b --- /dev/null +++ b/xenolinux-2.4.25-sparse/arch/xeno/drivers/evtchn/Makefile @@ -0,0 +1,3 @@ +O_TARGET := evtchn.o +obj-y := xl_evtchn.o +include $(TOPDIR)/Rules.make diff --git a/xenolinux-2.4.25-sparse/arch/xeno/drivers/evtchn/xl_evtchn.c b/xenolinux-2.4.25-sparse/arch/xeno/drivers/evtchn/xl_evtchn.c new file mode 100644 index 0000000000..82af52ec8c --- /dev/null +++ b/xenolinux-2.4.25-sparse/arch/xeno/drivers/evtchn/xl_evtchn.c @@ -0,0 +1,403 @@ +/****************************************************************************** + * xl_evtchn.c + * + * Xenolinux driver for receiving and demuxing event-channel signals. + * + * Copyright (c) 2004, K A Fraser + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/major.h> +#include <linux/proc_fs.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/stat.h> +#include <linux/poll.h> + +typedef void (*evtchn_receiver_t)(unsigned int); +#define PORT_NORMAL 0x0000 +#define PORT_DISCONNECT 0x8000 +#define PORTIDX_MASK 0x7fff + +/* /dev/xeno/evtchn resides at device number major=10, minor=200 */ +#define EVTCHN_MINOR 200 + +/* NB. This must be shared amongst drivers if more things go in /dev/xeno */ +static devfs_handle_t xeno_dev_dir; + +/* Only one process may open /dev/xeno/evtchn at any time. */ +static unsigned long evtchn_dev_inuse; + +/* Notification ring, accessed via /dev/xeno/evtchn. */ +static u16 *ring; +static unsigned int ring_cons, ring_prod; + +/* Processes wait on this queue via /dev/xeno/evtchn when ring is empty. */ +static DECLARE_WAIT_QUEUE_HEAD(evtchn_wait); +static struct fasync_struct *evtchn_async_queue; + +static evtchn_receiver_t rx_fns[1024]; + +static u32 pend_outstanding[32]; +static u32 disc_outstanding[32]; + +static spinlock_t lock; + +int evtchn_request_port(unsigned int port, evtchn_receiver_t rx_fn) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&lock, flags); + + if ( rx_fns[port] != NULL ) + { + printk(KERN_ALERT "Event channel port %d already in use.\n", port); + rc = -EINVAL; + } + else + { + rx_fns[port] = rx_fn; + rc = 0; + } + + spin_unlock_irqrestore(&lock, flags); + + return rc; +} + +int evtchn_free_port(unsigned int port) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&lock, flags); + + if ( rx_fns[port] == NULL ) + { + printk(KERN_ALERT "Event channel port %d not in use.\n", port); + rc = -EINVAL; + } + else + { + rx_fns[port] = NULL; + rc = 0; + } + + spin_unlock_irqrestore(&lock, flags); + + return rc; +} + +/* + * NB. Clearing port can race a notification from remote end. Caller must + * therefore recheck notification status on return to avoid missing events. + */ +void evtchn_clear_port(unsigned int port) +{ + unsigned int p = port & PORTIDX_MASK; + if ( unlikely(port & PORT_DISCONNECT) ) + { + clear_bit(p, &HYPERVISOR_shared_info->event_channel_disc[0]); + wmb(); /* clear the source first, then our quenchmask */ + clear_bit(p, &disc_outstanding[0]); + } + else + { + clear_bit(p, &HYPERVISOR_shared_info->event_channel_pend[0]); + wmb(); /* clear the source first, then our quenchmask */ + clear_bit(p, &pend_outstanding[0]); + } +} + +static inline void process_bitmask(u32 *sel, + u32 *mask, + u32 *outstanding, + unsigned int port_subtype) +{ + unsigned long l1, l2; + unsigned int l1_idx, l2_idx, port; + + l1 = xchg(sel, 0); + while ( (l1_idx = ffs(l1)) != 0 ) + { + l1_idx--; + l1 &= ~(1 << l1_idx); + + l2 = mask[l1_idx] & ~outstanding[l1_idx]; + outstanding[l1_idx] |= l2; + while ( (l2_idx = ffs(l2)) != 0 ) + { + l2_idx--; + l2 &= ~(1 << l2_idx); + + port = (l1_idx * 32) + l2_idx; + if ( rx_fns[port] != NULL ) + { + (*rx_fns[port])(port | port_subtype); + } + else if ( ring != NULL ) + { + ring[ring_prod] = (u16)(port | port_subtype); + if ( ring_cons == ring_prod++ ) + { + wake_up_interruptible(&evtchn_wait); + kill_fasync(&evtchn_async_queue, SIGIO, POLL_IN); + } + } + } + } + +} + +static void evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + shared_info_t *si = HYPERVISOR_shared_info; + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + + process_bitmask(&si->event_channel_pend_sel, + &si->event_channel_pend[0], + &pend_outstanding[0], + PORT_NORMAL); + + process_bitmask(&si->event_channel_disc_sel, + &si->event_channel_disc[0], + &disc_outstanding[0], + PORT_DISCONNECT); + + spin_unlock_irqrestore(&lock, flags); +} + +static ssize_t evtchn_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + int rc; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&evtchn_wait, &wait); + + for ( ; ; ) + { + set_current_state(TASK_INTERRUPTIBLE); + + if ( ring_cons != ring_prod ) + break; + + if ( file->f_flags & O_NONBLOCK ) + { + rc = -EAGAIN; + goto out; + } + + if ( signal_pending(current) ) + { + rc = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + rc = -EINVAL; + if ( count >= sizeof(ring_prod) ) + rc = put_user(ring_prod, (unsigned int *)buf); + if ( rc == 0 ) + rc = sizeof(ring_prod); + + out: + __set_current_state(TASK_RUNNING); + remove_wait_queue(&evtchn_wait, &wait); + return rc; +} + +static ssize_t evtchn_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + int rc = -EINVAL; + unsigned int new_cons = 0; + + if ( count >= sizeof(new_cons) ) + rc = get_user(new_cons, (unsigned int *)buf); + + if ( rc != 0 ) + return rc; + + rc = sizeof(new_cons); + + while ( ring_cons != new_cons ) + evtchn_clear_port(ring[ring_cons++]); + + return rc; +} + +static unsigned int evtchn_poll(struct file *file, poll_table *wait) +{ + unsigned int mask = POLLOUT | POLLWRNORM; + poll_wait(file, &evtchn_wait, wait); + if ( ring_cons != ring_prod ) + mask |= POLLIN | POLLRDNORM; + return mask; +} + +static int evtchn_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Caller must map a single page of memory from 'file offset' zero. */ + if ( (vma->vm_pgoff != 0) || ((vma->vm_end - vma->vm_start) != PAGE_SIZE) ) + return -EINVAL; + + /* Not a pageable area. */ + vma->vm_flags |= VM_RESERVED; + + if ( remap_page_range(vma->vm_start, 0, PAGE_SIZE, vma->vm_page_prot) ) + return -EAGAIN; + + return 0; +} + +static int evtchn_fasync(int fd, struct file *filp, int on) +{ + return fasync_helper(fd, filp, on, &evtchn_async_queue); +} + +static int evtchn_open(struct inode *inode, struct file *filp) +{ + u16 *_ring; + u32 m; + unsigned int i, j; + + if ( test_and_set_bit(0, &evtchn_dev_inuse) ) + return -EBUSY; + + /* Allocate outside locked region so that we can use GFP_KERNEL. */ + if ( (_ring = (u16 *)get_free_page(GFP_KERNEL)) == NULL ) + return -ENOMEM; + + spin_lock_irq(&lock); + + ring = _ring; + + /* Initialise the ring with currently outstanding notifications. */ + ring_cons = ring_prod = 0; + for ( i = 0; i < 32; i++ ) + { + m = pend_outstanding[i]; + while ( (j = ffs(m)) != 0 ) + { + m &= ~(1 << --j); + if ( rx_fns[(i * 32) + j] == NULL ) + ring[ring_prod++] = (u16)(((i * 32) + j) | PORT_NORMAL); + } + + m = disc_outstanding[i]; + while ( (j = ffs(m)) != 0 ) + { + m &= ~(1 << --j); + if ( rx_fns[(i * 32) + j] == NULL ) + ring[ring_prod++] = (u16)(((i * 32) + j) | PORT_DISCONNECT); + } + } + + spin_unlock_irq(&lock); + + MOD_INC_USE_COUNT; + + return 0; +} + +static int evtchn_release(struct inode *inode, struct file *filp) +{ + spin_lock_irq(&lock); + if ( ring != NULL ) + { + free_page((unsigned long)ring); + ring = NULL; + } + spin_unlock_irq(&lock); + + evtchn_dev_inuse = 0; + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct file_operations evtchn_fops = { + owner: THIS_MODULE, + read: evtchn_read, + write: evtchn_write, + poll: evtchn_poll, + mmap: evtchn_mmap, + fasync: evtchn_fasync, + open: evtchn_open, + release: evtchn_release +}; + +static struct miscdevice evtchn_miscdev = { + minor: EVTCHN_MINOR, + name: "evtchn", + fops: &evtchn_fops +}; + +static int __init init_module(void) +{ + devfs_handle_t symlink_handle; + int err, pos; + char link_dest[64]; + + /* (DEVFS) create '/dev/misc/evtchn'. */ + err = misc_register(&evtchn_miscdev); + if ( err != 0 ) + { + printk(KERN_ALERT "Could not register /dev/misc/evtchn\n"); + return err; + } + + /* (DEVFS) create directory '/dev/xeno'. */ + xeno_dev_dir = devfs_mk_dir(NULL, "xeno", NULL); + + /* (DEVFS) &link_dest[pos] == '../misc/evtchn'. */ + pos = devfs_generate_path(evtchn_miscdev.devfs_handle, + &link_dest[3], + sizeof(link_dest) - 3); + if ( pos >= 0 ) + strncpy(&link_dest[pos], "../", 3); + + /* (DEVFS) symlink '/dev/xeno/evtchn' -> '../misc/evtchn'. */ + (void)devfs_mk_symlink(xeno_dev_dir, + "evtchn", + DEVFS_FL_DEFAULT, + &link_dest[pos], + &symlink_handle, + NULL); + + /* (DEVFS) automatically destroy the symlink with its destination. */ + devfs_auto_unregister(evtchn_miscdev.devfs_handle, symlink_handle); + + err = request_irq(_EVENT_EVTCHN, evtchn_interrupt, 0, "evtchn", NULL); + if ( err != 0 ) + { + printk(KERN_ALERT "Could not allocate evtchn receive interrupt\n"); + return err; + } + + return 0; +} + +static void cleanup_module(void) +{ + free_irq(_EVENT_EVTCHN, NULL); + misc_deregister(&evtchn_miscdev); +} + +module_init(init_module); +module_exit(cleanup_module); diff --git a/xenolinux-2.4.25-sparse/arch/xeno/drivers/network/network.c b/xenolinux-2.4.25-sparse/arch/xeno/drivers/network/network.c index 29f7c2224f..95b4c45501 100644 --- a/xenolinux-2.4.25-sparse/arch/xeno/drivers/network/network.c +++ b/xenolinux-2.4.25-sparse/arch/xeno/drivers/network/network.c @@ -529,7 +529,7 @@ static struct notifier_block notifier_inetdev = { }; -int __init init_module(void) +static int __init init_module(void) { int i, fixmap_idx=-1, err; struct net_device *dev; diff --git a/xenolinux-2.4.25-sparse/arch/xeno/kernel/hypervisor.c b/xenolinux-2.4.25-sparse/arch/xeno/kernel/hypervisor.c index 3f414e9876..8e7e144137 100644 --- a/xenolinux-2.4.25-sparse/arch/xeno/kernel/hypervisor.c +++ b/xenolinux-2.4.25-sparse/arch/xeno/kernel/hypervisor.c @@ -18,8 +18,6 @@ int nr_multicall_ents = 0; static unsigned long event_mask = 0; -void frobb(void) {} - void do_hypervisor_callback(struct pt_regs *regs) { unsigned long events, flags; |