#include #include #include #include #include #include #include #include #include #if 1 #define dprintk(fmt...) printk(fmt) #else #define dprintk(fmt...) #endif LIST_HEAD(aiotest_devs); static struct class *aiotest_class = NULL; int aiotest_major = 0; MODULE_AUTHOR("Sergey Temerkhanov"); MODULE_DESCRIPTION("AIO tester module"); MODULE_LICENSE("GPL"); struct aiotest_dev { struct list_head list; wait_queue_head_t waitq; struct cdev cdev; atomic_t opened; int index; struct delayed_work work; }; #define AIOTEST_NUMDEVS 1 //---------------------------------------------------------------------------- static int aiotest_open(struct inode *inode, struct file *file) { struct aiotest_dev *dev; dev = container_of(inode->i_cdev, struct aiotest_dev, cdev); file->private_data = dev; if (!file->private_data) return -ENODEV; if (atomic_read(&dev->opened)) return -EBUSY; atomic_inc(&dev->opened); return 0; } static int aiotest_release(struct inode *inode, struct file *file) { struct aiotest_dev *dev; dev = file->private_data; if (!dev) return -ENODEV; atomic_dec(&dev->opened); return 0; } int aiotest_cancel(struct kiocb* iocb, struct io_event* evt) { printk("%s: %d: iocb: %p, iocb->ki_users: %d\n", __FUNCTION__, __LINE__, iocb, iocb->ki_users); aio_put_req(iocb); printk("%s: %d: iocb: %p, iocb->ki_users: %d\n", __FUNCTION__, __LINE__, iocb, iocb->ki_users); return 0; } ssize_t aiotest_rw_ki_cancel(struct kiocb *iocb, const struct iovec* iov, unsigned long count, loff_t off) { printk("%s: %d: iocb: %p, iocb->ki_users: %d\n", __FUNCTION__, __LINE__, iocb, iocb->ki_users); iocb->ki_cancel = aiotest_cancel; return -EIOCBQUEUED; } ssize_t aiotest_rw_nocancel(struct kiocb *iocb, const struct iovec* iov, unsigned long count, loff_t off) { printk("%s: %d: iocb: %p, iocb->ki_users: %d\n", __FUNCTION__, __LINE__, iocb, iocb->ki_users); return -EIOCBQUEUED; } ssize_t aiotest_rw_setcancelled(struct kiocb *iocb, const struct iovec* iov, unsigned long count, loff_t off) { printk("%s: %d: iocb: %p, iocb->ki_users: %d\n", __FUNCTION__, __LINE__, iocb, iocb->ki_users); kiocbSetCancelled(iocb); return -EINTR; } ssize_t aiotest_rw_delayed(struct kiocb *iocb, const struct iovec* iov, unsigned long count, loff_t off) { printk("%s: %d: iocb: %p, iocb->ki_users: %d\n", __FUNCTION__, __LINE__, iocb, iocb->ki_users); iocb->ki_cancel = aiotest_cancel; schedule_timeout(HZ); return -EIOCBQUEUED; } // --------------------------------------------------------------------------- static struct file_operations aiotest_fops_nocancel = { .owner = THIS_MODULE, .open = aiotest_open, .release = aiotest_release, .aio_read = aiotest_rw_nocancel, .aio_write = aiotest_rw_nocancel, }; static struct file_operations aiotest_fops_ki_cancel = { .owner = THIS_MODULE, .open = aiotest_open, .release = aiotest_release, .aio_read = aiotest_rw_ki_cancel, .aio_write = aiotest_rw_ki_cancel, }; static struct file_operations aiotest_fops_setcancelled = { .owner = THIS_MODULE, .open = aiotest_open, .release = aiotest_release, .aio_read = aiotest_rw_setcancelled, .aio_write = aiotest_rw_setcancelled, }; static struct file_operations aiotest_fops_delayed = { .owner = THIS_MODULE, .open = aiotest_open, .release = aiotest_release, .aio_read = aiotest_rw_delayed, .aio_write = aiotest_rw_delayed, }; static void aiotest_cleanup(struct aiotest_dev *dev) { if (!dev) return; device_destroy(aiotest_class, dev->cdev.dev); cdev_del(&dev->cdev); list_del(&dev->list); kfree(dev); }; static int init_aiotest_dev(int index, struct file_operations *fops) { struct aiotest_dev *dev; int res; dev = kzalloc(sizeof(struct aiotest_dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->index = index; device_create(aiotest_class, NULL, MKDEV(aiotest_major, dev->index), NULL, "aiotest%d", index); cdev_init(&dev->cdev, fops); res = cdev_add(&dev->cdev, MKDEV(aiotest_major, dev->index), 1); if (res) { aiotest_cleanup(dev); return res; } init_waitqueue_head(&dev->waitq); atomic_set(&dev->opened, 0); list_add_tail(&dev->list, &aiotest_devs); return 0; } // --------------------------------------------------------------------------- static void __exit aiodrv_cleanup(void); static int __init aiodrv_init(void) { dev_t device; int res; res = alloc_chrdev_region(&device, 0, AIOTEST_NUMDEVS, "aiotest"); aiotest_major = MAJOR(device); if (res < 0) { printk(KERN_ERR "Cannot get major number for aiotest\n"); return res; } aiotest_class = class_create(THIS_MODULE, "aiotest"); init_aiotest_dev(0, &aiotest_fops_nocancel); init_aiotest_dev(1, &aiotest_fops_ki_cancel); init_aiotest_dev(2, &aiotest_fops_setcancelled); init_aiotest_dev(3, &aiotest_fops_delayed); return 0; }; static void __exit aiodrv_cleanup(void) { struct aiotest_dev *dev, *next; list_for_each_entry_safe(dev, next, &aiotest_devs, list) { aiotest_cleanup(dev); } if (aiotest_class) class_destroy(aiotest_class); unregister_chrdev_region(MKDEV(aiotest_major, 0), AIOTEST_NUMDEVS); }; module_init(aiodrv_init); module_exit(aiodrv_cleanup);