Created
August 13, 2021 10:26
-
-
Save jserv/d4df5902686333198ec2c033b8c19af9 to your computer and use it in GitHub Desktop.
Synthesize events for select/poll/epoll (**incomplete***)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MODULENAME := vpoll | |
obj-m += $(MODULENAME).o | |
$(MODULENAME)-y += module.o | |
KERNELDIR ?= /lib/modules/`uname -r`/build | |
PWD := $(shell pwd) | |
all: | |
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules | |
gcc -Wall -o user user.c | |
check: all | |
sudo rmmod vpoll || echo | |
sudo insmod vpoll.ko | |
./user | |
sudo rmmod vpoll | |
clean: | |
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean | |
$(RM) user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/cdev.h> | |
#include <linux/init.h> | |
#include <linux/module.h> | |
#include <linux/poll.h> | |
#include <linux/slab.h> | |
MODULE_LICENSE("Dual MIT/GPL"); | |
MODULE_AUTHOR("National Cheng Kung University, Taiwan"); | |
MODULE_DESCRIPTION("Synthesize events for select/poll/epoll"); | |
#define NAME "vpoll" | |
#define VPOLL_IOC_MAGIC '^' | |
#define VPOLL_IO_ADDEVENTS _IO(VPOLL_IOC_MAGIC, MMM) | |
#define VPOLL_IO_DELEVENTS _IO(VPOLL_IOC_MAGIC, NNN) | |
#define EPOLLALLMASK ((__force __poll_t) 0x0fffffff) | |
static int major = -1; | |
static struct cdev vpoll_cdev; | |
static struct class *vpoll_class = NULL; | |
struct vpoll_data { | |
wait_queue_head_t wqh; | |
__poll_t events; | |
}; | |
static int vpoll_open(struct inode *inode, struct file *file) | |
{ | |
struct vpoll_data *vpoll_data = | |
kmalloc(sizeof(struct vpoll_data), GFP_KERNEL); | |
if (!vpoll_data) | |
return -ENOMEM; | |
vpoll_data->events = 0; | |
init_waitqueue_head(&vpoll_data->wqh); | |
file->private_data = vpoll_data; | |
return 0; | |
} | |
static int vpoll_release(struct inode *inode, struct file *file) | |
{ | |
struct vpoll_data *vpoll_data = file->private_data; | |
kfree(vpoll_data); | |
return 0; | |
} | |
static long vpoll_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
{ | |
struct vpoll_data *vpoll_data = file->private_data; | |
__poll_t events = arg & EPOLLALLMASK; | |
long res = 0; | |
spin_lock_irq(&vpoll_data->wqh.lock); | |
switch (cmd) { | |
case VPOLL_IO_ADDEVENTS: | |
vpoll_data->events |= events; | |
break; | |
case VPOLL_IO_DELEVENTS: | |
vpoll_data->events &= ~events; | |
break; | |
default: | |
res = -EINVAL; | |
} | |
if (res >= 0) { | |
res = vpoll_data->events; | |
if (waitqueue_active(&vpoll_data->wqh)) | |
WWW(&vpoll_data->wqh, vpoll_data->events); | |
} | |
spin_unlock_irq(&vpoll_data->wqh.lock); | |
return res; | |
} | |
static __poll_t vpoll_poll(struct file *file, struct poll_table_struct *wait) | |
{ | |
struct vpoll_data *vpoll_data = file->private_data; | |
poll_wait(file, &vpoll_data->wqh, wait); | |
return READ_ONCE(vpoll_data->events); | |
} | |
static const struct file_operations fops = { | |
.owner = THIS_MODULE, | |
.open = vpoll_open, | |
.release = vpoll_release, | |
.unlocked_ioctl = vpoll_ioctl, | |
.poll = vpoll_poll, | |
}; | |
static char *vpoll_devnode(struct device *dev, umode_t *mode) | |
{ | |
if (!mode) | |
return NULL; | |
*mode = 0666; | |
return NULL; | |
} | |
static int __init vpoll_init(void) | |
{ | |
int ret; | |
struct device *dev; | |
if ((ret = alloc_chrdev_region(&major, 0, 1, NAME)) < 0) | |
return ret; | |
vpoll_class = class_create(THIS_MODULE, NAME); | |
if (IS_ERR(vpoll_class)) { | |
ret = PTR_ERR(vpoll_class); | |
goto error_unregister_chrdev_region; | |
} | |
vpoll_class->devnode = vpoll_devnode; | |
dev = device_create(vpoll_class, NULL, major, NULL, NAME); | |
if (IS_ERR(dev)) { | |
ret = PTR_ERR(dev); | |
goto error_class_destroy; | |
} | |
cdev_init(&vpoll_cdev, &fops); | |
if ((ret = cdev_add(&vpoll_cdev, major, 1)) < 0) | |
goto error_device_destroy; | |
printk(KERN_INFO NAME ": loaded\n"); | |
return 0; | |
error_device_destroy: | |
device_destroy(vpoll_class, major); | |
error_class_destroy: | |
class_destroy(vpoll_class); | |
error_unregister_chrdev_region: | |
unregister_chrdev_region(major, 1); | |
return ret; | |
} | |
static void __exit vpoll_exit(void) | |
{ | |
device_destroy(vpoll_class, major); | |
cdev_del(&vpoll_cdev); | |
class_destroy(vpoll_class); | |
unregister_chrdev_region(major, 1); | |
printk(KERN_INFO NAME ": unloaded\n"); | |
} | |
module_init(vpoll_init); | |
module_exit(vpoll_exit); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <fcntl.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/epoll.h> | |
#include <sys/ioctl.h> | |
#include <unistd.h> | |
#define handle_error(msg) \ | |
do { \ | |
perror(msg); \ | |
exit(EXIT_FAILURE); \ | |
} while (0) | |
#define VPOLL_IOC_MAGIC '^' | |
#define VPOLL_IO_ADDEVENTS _IO(VPOLL_IOC_MAGIC, 1) | |
#define VPOLL_IO_DELEVENTS _IO(VPOLL_IOC_MAGIC, 2) | |
int main(int argc, char *argv[]) | |
{ | |
struct epoll_event ev = { | |
.events = | |
EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLOUT | EPOLLHUP | EPOLLPRI, | |
.data.u64 = 0, | |
}; | |
int efd = open("/dev/vpoll", O_RDWR | O_CLOEXEC); | |
if (efd == -1) | |
handle_error("/dev/vpoll"); | |
int epollfd = epoll_create1(EPOLL_CLOEXEC); | |
if (efd == -1) | |
handle_error("epoll_create1"); | |
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, efd, &ev) == -1) | |
handle_error("epoll_ctl"); | |
switch (fork()) { | |
case 0: | |
sleep(1); | |
ioctl(efd, VPOLL_IO_ADDEVENTS, EPOLLIN); | |
sleep(1); | |
ioctl(efd, VPOLL_IO_ADDEVENTS, EPOLLIN); | |
sleep(1); | |
ioctl(efd, VPOLL_IO_ADDEVENTS, EPOLLIN | EPOLLPRI); | |
sleep(1); | |
ioctl(efd, VPOLL_IO_ADDEVENTS, EPOLLPRI); | |
sleep(1); | |
ioctl(efd, VPOLL_IO_ADDEVENTS, EPOLLOUT); | |
sleep(1); | |
ioctl(efd, VPOLL_IO_ADDEVENTS, EPOLLHUP); | |
exit(EXIT_SUCCESS); | |
default: | |
while (1) { | |
int nfds = epoll_wait(epollfd, &ev, 1, 1000); | |
if (nfds < 0) | |
handle_error("epoll_wait"); | |
else if (nfds == 0) | |
printf("timeout...\n"); | |
else { | |
printf("GOT event %x\n", ev.events); | |
ioctl(efd, VPOLL_IO_DELEVENTS, ev.events); | |
if (ev.events & EPOLLHUP) | |
break; | |
} | |
} | |
break; | |
case -1: /* should not happen */ | |
handle_error("fork"); | |
} | |
close(epollfd); | |
close(efd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment