mirror of
https://github.com/darlinghq/darling-libkqueue.git
synced 2024-11-23 03:39:51 +00:00
d231f92742
git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/trunk@222 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
271 lines
7.6 KiB
C
271 lines
7.6 KiB
C
/*-
|
|
* Copyright (c) 2010 Mark Heily <mark@heily.com>
|
|
*
|
|
* Includes portions of /usr/src/sys/kern/kern_event.c which is
|
|
*
|
|
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
|
|
* Copyright 2004 John-Mark Gurney <jmg@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/* Portions based on
|
|
$FreeBSD: src/sys/kern/kern_event.c,v 1.126.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $
|
|
$FreeBSD: src/sys/sys/eventvar.h,v 1.6.30.1.2.1 2009/10/25 01:10:29 kensmith Exp $
|
|
*/
|
|
|
|
#include <linux/cdev.h>
|
|
#include <linux/device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/module.h>
|
|
#include <linux/rbtree.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include "../include/sys/event.h"
|
|
#include "queue.h"
|
|
|
|
struct kqueue;
|
|
struct kfilter;
|
|
struct knote;
|
|
|
|
static int kqueue_open (struct inode *inode, struct file *file);
|
|
static int kqueue_release (struct inode *inode, struct file *file);
|
|
static int kqueue_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg);
|
|
static ssize_t kqueue_read(struct file *file, char __user *buf,
|
|
size_t lbuf, loff_t *ppos);
|
|
static ssize_t kqueue_write(struct file *file, const char __user *buf,
|
|
size_t lbuf, loff_t *ppos);
|
|
|
|
struct file_operations fops = {
|
|
.owner = THIS_MODULE,
|
|
.ioctl = kqueue_ioctl,
|
|
.open = kqueue_open,
|
|
.release = kqueue_release,
|
|
.read = kqueue_read,
|
|
.write = kqueue_write,
|
|
};
|
|
|
|
struct kfilter {
|
|
struct rb_root kf_note;
|
|
};
|
|
|
|
struct kqueue {
|
|
spinlock_t kq_lock;
|
|
int kq_count; /* number of pending events */
|
|
struct kfilter kq_filt[EVFILT_SYSCOUNT];
|
|
};
|
|
|
|
#ifdef TODO
|
|
struct filterops {
|
|
int f_isfd; /* true if ident == filedescriptor */
|
|
int (*f_attach)(struct knote *kn);
|
|
void (*f_detach)(struct knote *kn);
|
|
int (*f_event)(struct knote *kn, long hint);
|
|
};
|
|
|
|
static struct kfilter {
|
|
struct filterops kf_fop;
|
|
int for_refcnt;
|
|
} sysfilt_ops[EVFILT_SYSCOUNT];
|
|
= {
|
|
{ &file_filtops }, /* EVFILT_READ */
|
|
{ &file_filtops }, /* EVFILT_WRITE */
|
|
{ &null_filtops }, /* EVFILT_AIO */
|
|
{ &file_filtops }, /* EVFILT_VNODE */
|
|
{ &proc_filtops }, /* EVFILT_PROC */
|
|
{ &sig_filtops }, /* EVFILT_SIGNAL */
|
|
{ &timer_filtops }, /* EVFILT_TIMER */
|
|
{ &file_filtops }, /* EVFILT_NETDEV */
|
|
{ &fs_filtops }, /* EVFILT_FS */
|
|
{ &null_filtops }, /* EVFILT_LIO */
|
|
};
|
|
#endif
|
|
|
|
static int major;
|
|
static struct class *kqueue_class;
|
|
static struct task_struct *kq_thread;
|
|
|
|
static struct kfilter *
|
|
kfilter_lookup(struct kqueue *kq, int filt)
|
|
{
|
|
if (filt > 0 || filt + EVFILT_SYSCOUNT < 0)
|
|
return NULL;
|
|
return &kq->kq_filt[~filt];
|
|
}
|
|
|
|
//only for sleeping during testing
|
|
#include <linux/delay.h>
|
|
static int kqueue_main(void *arg)
|
|
{
|
|
printk(KERN_INFO "kqueue thread started...\n");
|
|
while (!kthread_should_stop()) {
|
|
msleep(5000);
|
|
printk(KERN_INFO "kqueue thread awake...\n");
|
|
}
|
|
printk(KERN_INFO "kqueue stopping...\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int kqueue_open (struct inode *inode, struct file *file)
|
|
{
|
|
struct kqueue *kq;
|
|
int i;
|
|
|
|
printk("kqueue_open\n");
|
|
|
|
kq = kmalloc(sizeof(*kq), GFP_KERNEL);
|
|
if (kq == NULL) {
|
|
printk("kqueue: kmalloc failed\n");
|
|
return -1;
|
|
}
|
|
spin_lock_init(&kq->kq_lock);
|
|
for (i = 0; i < EVFILT_SYSCOUNT; i++)
|
|
kq->kq_filt[i].kf_note = RB_ROOT;
|
|
file->private_data = kq;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int kqueue_release (struct inode *inode, struct file *file)
|
|
{
|
|
printk("kqueue_release\n");
|
|
kfree(file->private_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int kqueue_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
int fd;
|
|
|
|
if (copy_from_user(&fd, (int *)arg, sizeof(int)))
|
|
return -EFAULT;
|
|
|
|
printk(KERN_INFO "added fd %d\n", fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t kqueue_read(struct file *file, char __user *buf,
|
|
size_t lbuf, loff_t *ppos)
|
|
{
|
|
struct kqueue *kq = file->private_data;
|
|
|
|
spin_lock(&kq->kq_lock);
|
|
//STUB
|
|
spin_unlock(&kq->kq_lock);
|
|
|
|
return sizeof(struct kevent);
|
|
}
|
|
|
|
static ssize_t kqueue_write(struct file *file, const char __user *buf,
|
|
size_t lbuf, loff_t *ppos)
|
|
{
|
|
struct kqueue *kq = file->private_data;
|
|
struct kevent kev;
|
|
struct kfilter *filt;
|
|
size_t i, nchanges;
|
|
|
|
if ((lbuf % sizeof(struct kevent)) != 0)
|
|
return -EINVAL;
|
|
nchanges = lbuf / sizeof(struct kevent);
|
|
|
|
for (i = 0; i < nchanges; i++) {
|
|
if (copy_from_user(&kev, (struct kevent *) buf, sizeof(kev)))
|
|
return -EFAULT;
|
|
|
|
filt = kfilter_lookup(kq, kev.filter);
|
|
if (filt == NULL)
|
|
return -EINVAL;
|
|
|
|
#ifdef DEADWOOD
|
|
spin_lock(&kq->kq_lock);
|
|
printk("%zu bytes, nchanges=%zu", lbuf, nchanges);
|
|
spin_unlock(&kq->kq_lock);
|
|
#endif
|
|
|
|
buf += sizeof(kev);
|
|
}
|
|
|
|
return sizeof(struct kevent);
|
|
}
|
|
|
|
static int __init kqueue_start(void)
|
|
{
|
|
int rv = 0;
|
|
|
|
printk(KERN_INFO "Loading kqueue module...\n");
|
|
|
|
/* Register as a character device */
|
|
major = register_chrdev(0, "kqueue", &fops);
|
|
if (major < 0) {
|
|
printk(KERN_WARNING "register_chrdev() failed");
|
|
return major;
|
|
}
|
|
|
|
/* Create /dev/kqueue */
|
|
kqueue_class = class_create(THIS_MODULE, "kqueue");
|
|
device_create(kqueue_class, NULL, MKDEV(major,0), NULL, "kqueue");
|
|
|
|
printk(KERN_INFO "Creating helper thread...\n");
|
|
kq_thread = kthread_create(kqueue_main, NULL, "kqueue");
|
|
if (IS_ERR(kq_thread)) {
|
|
rv = PTR_ERR(kq_thread);
|
|
goto err_out;
|
|
}
|
|
wake_up_process(kq_thread);
|
|
|
|
printk(KERN_INFO "Finished loading kqueue module...\n");
|
|
return rv;
|
|
|
|
err_out:
|
|
//TODO: cleanup
|
|
return rv;
|
|
}
|
|
|
|
static void __exit kqueue_end(void)
|
|
{
|
|
printk(KERN_INFO "Unloading kqueue module\n");
|
|
|
|
/* Remove /dev/kqueue */
|
|
device_destroy(kqueue_class, MKDEV(major,0));
|
|
class_destroy(kqueue_class);
|
|
unregister_chrdev(major, "kqueue");
|
|
|
|
kthread_stop(kq_thread);
|
|
}
|
|
|
|
module_init(kqueue_start);
|
|
module_exit(kqueue_end);
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
MODULE_AUTHOR("Mark Heily <mark@heily.com>");
|
|
MODULE_DESCRIPTION("kqueue(2) compatibility");
|