Messages in this thread Patch in this message | | | Date | Fri, 27 Mar 1998 17:52:38 -0800 (PST) | From | Robey Pointer <> | Subject | I/O completion ports for Linux |
| |
The following is a patch against Linux 2.1.84 to implement "I/O completion ports" similar to VMS/NT. It will probably patch against more recent kernels, with slight changes. (I used 84 because it was the last one I could boot from when I started the patch -- the next version of the patch will be against one of the 2.1.9x probably.)
An I/O completion port is a special fd that receives notification of completed I/O (hence the name...) from other fd's. You can start an I/O operation from one thread, and any other thread can pick up the result later, when the I/O is finished.
The general concept is: You open an IOCP, and associate file descriptors with it via ioctl(). Read/write operations on that fd will now always return EINPROGRESS, indicating that the I/O request has been queued. A read() on the IOCP will eventually pull out a data structure containing all the information from the original read/write call, plus the results.
Normal unix asynchronous I/O uses signals to notify a process/thread that some I/O operation won't block, but no information is given to the process beyond that signal. Solaris' aio_read/write (when they are implemented) will queue the request but will still use signals to notify the process of completion. Linux IOCP avoids signals altogether.
I've included a small test program, "iocp-test.c", to show how IOCP's work.
This patch is intended as a "proof of concept" only -- it works (for me), but isn't ready for prime time yet. Issues that need to be solved:
* In this patch, each IOCP can only have 15 pending I/O operations on it. In reality, applications would probably want to queue a lot more, and the queue should be allocated dynamically. Which also means that there will need to be a mechanism to keep users from abusing it by creating a bunch of IOCP's and sucking up lots of memory that way.
* read() on an IOCP is implemented, but not poll() yet. (Should be easy, I just haven't bothered yet.)
* The method of opening an IOCP is a total hack (creating a bogus flag in the open() syscall). A better way would probably be to create a device like "/dev/iocp" which hands out IOCPs, but I'm not familiar enough with Linux's device code to try this yet. (And I'm not entirely sure it can be done without assigning a different minor# to each IOCP, which would suck.)
* Only read() and write() syscalls are supported in this patch, and only for sockets. Like I said, it's just a proof of concept.
Please send comments and suggestions! This is my first attempt at a kernel patch. :)
Robey -- Robey Pointer | "So that's what an invisible barrier robey@lag.net | looks like." -Time Bandits http://www.lag.net/~robey | (join the 90's retro bandwagon early!) diff -ruN linux/fs/Makefile linux-iocp/fs/Makefile --- linux/fs/Makefile Wed Mar 25 19:34:58 1998 +++ linux-iocp/fs/Makefile Wed Mar 25 19:40:46 1998 @@ -13,7 +13,7 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \ - inode.o dcache.o attr.o bad_inode.o $(BINFMTS) + inode.o dcache.o attr.o bad_inode.o iocp.o $(BINFMTS) MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ diff -ruN linux/fs/iocp.c linux-iocp/fs/iocp.c --- linux/fs/iocp.c Wed Dec 31 16:00:00 1969 +++ linux-iocp/fs/iocp.c Thu Mar 26 17:57:08 1998 @@ -0,0 +1,392 @@ +/* + * linux/fs/iocp.c + * + * Copyright (C) 1998 Robey Pointer + */ + +#include <linux/vfs.h> +#include <linux/fs.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/file.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/iocp.h> +#include <asm/uaccess.h> + + +/* free up the completion queue */ +static int iocp_release(struct inode *inode, struct file *filp) +{ + struct iocp_private *pr = (struct iocp_private *)filp->private_data; + struct file *f; + + /* disassociate from any fd's that are still in my list */ + for (f = pr->next; f; f = f->f_notify_next) + f->f_notify = NULL; + /* get rid of the pesky inode that "pipe" made for me */ + free_page((unsigned long) PIPE_BASE(*inode)); + PIPE_BASE(*inode) = NULL; + kfree(pr); + filp->private_data = NULL; + return 0; +} + +/* is a given fd being watched by this iocp? */ +static int iocp_getwatch(struct file *filp, unsigned long arg) +{ + struct iocp_watch iw; + struct file *f; + int error; + + error = copy_from_user((void *)&iw, (void *)arg, + sizeof(struct iocp_watch)); + if (error) + return error; + f = fget(iw.fd); + if (!f) + return -EBADF; + iw.watch = (f->f_notify == filp); + error = copy_to_user((void *)arg, (void *)&iw, + sizeof(struct iocp_watch)); + fput(f); + return error; +} + +/* make a given fd be watched/not watched by this iocp */ +static int iocp_setwatch(struct file *filp, unsigned long arg) +{ + struct iocp_watch iw; + struct file *f; + int error; + + error = copy_from_user((void *)&iw, (void *)arg, + sizeof(struct iocp_watch)); + if (error) + return error; + f = fget(iw.fd); + if (!f) + return -EBADF; + if (f->f_flags & O_SPECIAL) + return -EINVAL; /* can't watch another iocp! */ + lock_kernel(); + /* no matter what, remove the old chain */ + if (f->f_notify) { + struct iocp_private *pr = (struct iocp_private *) + f->f_notify->private_data; + + if (f->f_notify_prev) + f->f_notify_prev->f_notify_next = f->f_notify_next; + if (f->f_notify_next) + f->f_notify_next->f_notify_prev = f->f_notify_prev; + if (pr->next == f) + pr->next = f->f_notify_next; + f->f_notify = NULL; + } + if (iw.watch) { + struct iocp_private *pr = (struct iocp_private *) + filp->private_data; + + f->f_notify = filp; + f->f_notify_next = pr->next; + f->f_notify_prev = NULL; + pr->next = f; + if (f->f_notify_next) + f->f_notify_next->f_notify_prev = f; + } + unlock_kernel(); + fput(f); + return 0; +} + +static int iocp_ioctl(struct inode *unused, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + switch(cmd) { + case IOCPGWATCH: + return iocp_getwatch(filp, arg); + case IOCPSWATCH: + return iocp_setwatch(filp, arg); + default: + return -EINVAL; + } +} + +static ssize_t iocp_read(struct file *filp, char *buf, size_t count, + loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct iocp_private *pr = (struct iocp_private *)filp->private_data; + struct iocp_io_user iou; + struct iocp_io io; + ssize_t ret = -EBADF; + unsigned short saved_flags; + long i; + int which, error; + + if (ppos != &filp->f_pos) + return -ESPIPE; + if (count < sizeof(struct iocp_io_user)) + return -EINVAL; + + do { + /* empty queue? */ + if (filp->f_flags & O_NONBLOCK) { + if (PIPE_LOCK(*inode)) + return -EAGAIN; + if (!(which = iocp_io_ready(pr))) + return -EAGAIN; + } + else while (!(which = iocp_io_ready(pr)) || + PIPE_LOCK(*inode)) { + if (signal_pending(current)) + return -ERESTARTSYS; + interruptible_sleep_on(&PIPE_WAIT(*inode)); + } + which--; + PIPE_LOCK(*inode)++; + iocp_pop(pr, which, &io); + + /* set the file non-blocking for now */ + saved_flags = io.f->f_flags; + io.f->f_flags |= O_NONBLOCK; + /* perform the requested operation */ + if (io.flags & IOCPQ_READ) + ret = ksys_pread(io.f, io.buffer, io.nbytes, + io.offset); + else if (io.flags & IOCPQ_WRITE) + ret = ksys_pwrite(io.f, io.buffer, io.nbytes, + io.offset); + io.f->f_flags = saved_flags; + PIPE_LOCK(*inode)--; + + if (ret == -EAGAIN) { + /* we were fooled. this isn't ready. */ + io.flags &= ~IOCPQ_READY; + iocp_push(pr, &io); + } + } while (ret == -EAGAIN); + + /* operation complete -- copy info into the user struct */ + iou.buffer = io.buffer; + iou.nbytes = io.nbytes; + iou.nresult = (ret < 0) ? 0 : ret; + iou.offset = *(io.offset); + iou.flags = io.flags & IOCPQ_RWMASK; + iou.error = (ret < 0) ? -ret : 0; + + /* i wish there was a better way to do this: */ + for (i = 0; (current->files->fd[i] != io.f) && (i < NR_OPEN); i++); + /* not sure what else i can do here: */ + if (i >= NR_OPEN) + i = -1; + iou.fd = i; + error = copy_to_user(buf, &iou, sizeof(struct iocp_io_user)); + if (error) + return error; + return sizeof(struct iocp_io_user); +} + +static ssize_t iocp_write(struct file *filp, const char *buf, size_t count, + loff_t *ppos) +{ + return -EBADF; +} + +static long long iocp_lseek(struct file *filp, long long offset, int orig) +{ + return -ESPIPE; +} + +/* file operations on an I/O completion port */ +struct file_operations iocp_file_ops = { + iocp_lseek, + iocp_read, + iocp_write, + NULL, /* readdir */ + NULL, /* poll */ + iocp_ioctl, + NULL, /* mmap */ + NULL, /* open */ + iocp_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change (whatever) */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +/***** functions called by the kernel internally *****/ + +/* create a new iocp */ +int iocp_new(unsigned long fd) +{ + struct file *f; + struct iocp_private *p; + struct inode *inode; + int error; + + error = -ENFILE; + f = get_empty_filp(); + if (!f) + goto no_files; + /* crazy inode/dentry stuff we don't really need */ + /* use pipe's inode structure since it matches pretty closely */ + inode = get_pipe_inode(); + if (!inode) + goto cleanup_file; + /* fix one small thing that get_pipe_inode() does */ + inode->i_op->default_file_ops = &iocp_file_ops; + error = -ENOMEM; + f->f_dentry = dget(d_alloc_root(inode, NULL)); + if (!f->f_dentry) + goto cleanup_inode; + f->f_flags = O_SPECIAL | OSP_IOCP; + f->f_mode = FMODE_READ; + f->f_pos = 0; + f->f_op = &iocp_file_ops; + p = (struct iocp_private *)kmalloc(sizeof(struct iocp_private), + GFP_KERNEL); + if (!p) + goto cleanup_file; + spin_lock_init(p->lock); + p->inq_tail = 0; + p->next = NULL; + f->private_data = p; + current->files->fd[fd] = f; /* fd_install(fd, f); */ + return 0; + +cleanup_inode: + free_page((unsigned long) PIPE_BASE(*inode)); + iput(inode); +cleanup_file: + put_filp(f); +no_files: + return error; +} + +/* queue an i/o request to the associated iocp of a file. + * NOTE: a different interface will be needed once pread/pwrite are supported, + * since those pass an loff_t, not an loff_t*. + */ +int iocp_addq(struct file *filp, const void *buffer, size_t nbytes, + loff_t *offset, unsigned int flags) +{ + struct iocp_private *pr = (struct iocp_private *) + filp->f_notify->private_data; + struct inode *inode = filp->f_notify->f_dentry->d_inode; + + spin_lock(pr->lock); + if (pr->inq_tail >= IOCP_MAXQ) { + spin_unlock(pr->lock); + return -ENOMEM; /* is there a better error? */ + } + pr->inq[pr->inq_tail].f = filp; + pr->inq[pr->inq_tail].buffer = (void *)buffer; + pr->inq[pr->inq_tail].nbytes = nbytes; + pr->inq[pr->inq_tail].offset = offset; + pr->inq[pr->inq_tail].task = current; + /* it might be ready right now! can't hurt to check... */ + pr->inq[pr->inq_tail].flags = flags | IOCPQ_READY; + pr->inq_tail++; + spin_unlock(pr->lock); + wake_up_interruptible(&PIPE_WAIT(*inode)); + return -EINPROGRESS; +} + +/* disconnect this file from its iocp */ +void iocp_put(struct file *filp) +{ + struct iocp_private *pr = (struct iocp_private *) + filp->f_notify->private_data; + int i; + + /* scan the iocp queue for pending operations on this file, + * and squash them. + */ + spin_lock(pr->lock); + for (i = 0; i < pr->inq_tail; i++) { + if (pr->inq[i].f == filp) { + if (pr->inq_tail > i+1) + memcpy(&(pr->inq[i]), + &(pr->inq[pr->inq_tail-1]), + sizeof(struct iocp_io)); + pr->inq_tail--; + i--; + } + } + /* remove this file from the watch list */ + if (filp->f_notify_prev) + filp->f_notify_prev->f_notify_next = filp->f_notify_next; + if (filp->f_notify_next) + filp->f_notify_next->f_notify_prev = filp->f_notify_prev; + if (pr->next == filp) + pr->next = filp->f_notify_next; + spin_unlock(pr->lock); +} + +/* notify the iocp engine that a read/write operation will probably + * succeed for this file + */ +void iocp_notify(struct file *filp, unsigned int flags) +{ + struct iocp_private *pr = (struct iocp_private *) + filp->f_notify->private_data; + struct inode *inode = filp->f_notify->f_dentry->d_inode; + int awaken = 0, i; + + /* all the queued operations for one file COULD be for small + * amounts, so we just mark them all as ready. if we're wrong, + * the iocp_read() handler will find out. + */ + spin_lock(pr->lock); + for (i = 0; i < pr->inq_tail; i++) { + if ((pr->inq[i].f == filp) && + ((pr->inq[i].flags & IOCPQ_RWMASK) == flags)) { + awaken = 1; + pr->inq[i].flags |= IOCPQ_READY; + } + } + spin_unlock(pr->lock); + if (awaken) + wake_up_interruptible(&PIPE_WAIT(*inode)); +} + +/* any files ready? */ +int iocp_io_ready(struct iocp_private *pr) +{ + int i; + + for (i = 0; i < pr->inq_tail; i++) + if (pr->inq[i].flags & IOCPQ_READY) + return i+1; + return 0; +} + +void iocp_pop(struct iocp_private *pr, int which, struct iocp_io *io) +{ + spin_lock(pr->lock); + memcpy(io, &(pr->inq[which]), sizeof(struct iocp_io)); + if (which+1 < pr->inq_tail) + memcpy(&(pr->inq[which]), &(pr->inq[pr->inq_tail-1]), + sizeof(struct iocp_io)); + pr->inq_tail--; + spin_unlock(pr->lock); +} + +/* only called when preceded by an iocp_pop, so there's always space */ +void iocp_push(struct iocp_private *pr, struct iocp_io *io) +{ + spin_lock(pr->lock); + if (pr->inq_tail >= IOCP_MAXQ) { + /* how'd that happen? */ + spin_unlock(pr->lock); + return; + } + memcpy(&(pr->inq[pr->inq_tail]), io, sizeof(struct iocp_io)); + pr->inq_tail++; + spin_unlock(pr->lock); +} + diff -ruN linux/fs/open.c linux-iocp/fs/open.c --- linux/fs/open.c Wed Mar 25 19:34:58 1998 +++ linux-iocp/fs/open.c Thu Mar 26 13:54:18 1998 @@ -20,6 +20,8 @@ #include <linux/file.h> #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/iocp.h> +#include <linux/slab.h> #include <asm/uaccess.h> #include <asm/bitops.h> @@ -692,8 +694,21 @@ error = get_unused_fd(); if (error < 0) goto out; - fd = error; + + /* check for request for iocp */ + if (flags & O_SPECIAL) { + if ((flags & 255) != OSP_IOCP) { + error = -ENOENT; + goto out_fail; + } + error = iocp_new(fd); + if (error) + goto out_fail; + error = fd; + goto out; + } + tmp = getname(filename); error = PTR_ERR(tmp); if (!IS_ERR(tmp)) { @@ -704,6 +719,7 @@ goto out; } } +out_fail: put_unused_fd(fd); out: unlock_kernel(); @@ -736,6 +752,8 @@ if (filp->f_op && filp->f_op->release) error = filp->f_op->release(inode, filp); + if (filp->f_notify) + iocp_put(filp); filp->f_dentry = NULL; if (filp->f_mode & FMODE_WRITE) put_write_access(inode); diff -ruN linux/fs/pipe.c linux-iocp/fs/pipe.c --- linux/fs/pipe.c Wed Mar 25 19:34:58 1998 +++ linux-iocp/fs/pipe.c Wed Mar 25 19:40:46 1998 @@ -385,7 +385,7 @@ NULL }; -static struct inode * get_pipe_inode(void) +struct inode * get_pipe_inode(void) { extern struct inode_operations pipe_inode_operations; struct inode *inode = get_empty_inode(); diff -ruN linux/fs/read_write.c linux-iocp/fs/read_write.c --- linux/fs/read_write.c Wed Mar 25 19:34:58 1998 +++ linux-iocp/fs/read_write.c Thu Mar 26 13:54:18 1998 @@ -17,6 +17,7 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/limits.h> +#include <linux/iocp.h> #include <asm/uaccess.h> @@ -134,7 +135,10 @@ ret = -EINVAL; if (!file->f_op || !(read = file->f_op->read)) goto out; - ret = read(file, buf, count, &file->f_pos); + if (file->f_notify) + ret = iocp_addq(file, buf, count, &(file->f_pos), IOCPQ_READ); + else + ret = read(file, buf, count, &file->f_pos); out: fput(file); bad_file: @@ -167,7 +171,10 @@ goto out; down(&inode->i_sem); - ret = write(file, buf, count, &file->f_pos); + if (file->f_notify) + ret = iocp_addq(file, buf, count, &(file->f_pos), IOCPQ_WRITE); + else + ret = write(file, buf, count, &file->f_pos); up(&inode->i_sem); out: fput(file); @@ -320,55 +327,63 @@ lseek back to original location. They fail just like lseek does on non-seekable files. */ -asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, - size_t count, loff_t pos) +ssize_t ksys_pread(struct file *file, char *buf, size_t count, loff_t *pos) { ssize_t ret; - struct file * file; ssize_t (*read)(struct file *, char *, size_t, loff_t *); lock_kernel(); ret = -EBADF; - file = fget(fd); - if (!file) - goto bad_file; if (!(file->f_mode & FMODE_READ)) goto out; ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode, - file, pos, count); + file, *pos, count); if (ret) goto out; ret = -EINVAL; if (!file->f_op || !(read = file->f_op->read)) goto out; - if (pos < 0) + if (*pos < 0) goto out; - ret = read(file, buf, count, &pos); + ret = read(file, buf, count, pos); out: + unlock_kernel(); + return ret; +} + +asmlinkage ssize_t sys_pread(unsigned int fd, char *buf, size_t count, + loff_t pos) +{ + struct file *file; + ssize_t ret; + + lock_kernel(); + ret = -EBADF; + file = fget(fd); + if (!file) + goto bad_file; + ret = ksys_pread(file, buf, count, &pos); fput(file); + bad_file: unlock_kernel(); return ret; } -asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, - size_t count, loff_t pos) +ssize_t ksys_pwrite(struct file *file, const char *buf, size_t count, + loff_t *pos) { ssize_t ret; - struct file * file; ssize_t (*write)(struct file *, const char *, size_t, loff_t *); lock_kernel(); ret = -EBADF; - file = fget(fd); - if (!file) - goto bad_file; if (!(file->f_mode & FMODE_WRITE)) goto out; ret = locks_verify_area(FLOCK_VERIFY_WRITE, file->f_dentry->d_inode, - file, pos, count); + file, *pos, count); if (ret) goto out; ret = -EINVAL; @@ -378,12 +393,30 @@ goto out; down(&file->f_dentry->d_inode->i_sem); - ret = write(file, buf, count, &pos); + ret = write(file, buf, count, pos); up(&file->f_dentry->d_inode->i_sem); out: + unlock_kernel(); + return ret; +} + +asmlinkage ssize_t sys_pwrite(unsigned int fd, const char *buf, + size_t count, loff_t pos) +{ + ssize_t ret; + struct file *file; + + lock_kernel(); + ret = -EBADF; + file = fget(fd); + if (!file) + goto bad_file; + ret = ksys_pwrite(file, buf, count, &pos); fput(file); + bad_file: unlock_kernel(); return ret; } + diff -ruN linux/include/asm/fcntl.h linux-iocp/include/asm/fcntl.h --- linux/include/asm/fcntl.h Wed Mar 25 19:35:11 1998 +++ linux-iocp/include/asm/fcntl.h Wed Mar 25 20:10:47 1998 @@ -16,6 +16,10 @@ #define O_NDELAY O_NONBLOCK #define O_SYNC 010000 #define FASYNC 020000 /* fcntl, for BSD compatibility */ +#define O_SPECIAL 040000 /* see below */ + +/* mask off lower 8 bits for O_SPECIAL to determine type: */ +#define OSP_IOCP 1 /* I/O completion notification */ #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get f_flags */ diff -ruN linux/include/asm-i386/fcntl.h linux-iocp/include/asm-i386/fcntl.h --- linux/include/asm-i386/fcntl.h Wed Mar 25 19:35:11 1998 +++ linux-iocp/include/asm-i386/fcntl.h Wed Mar 25 20:10:47 1998 @@ -16,6 +16,10 @@ #define O_NDELAY O_NONBLOCK #define O_SYNC 010000 #define FASYNC 020000 /* fcntl, for BSD compatibility */ +#define O_SPECIAL 040000 /* see below */ + +/* mask off lower 8 bits for O_SPECIAL to determine type: */ +#define OSP_IOCP 1 /* I/O completion notification */ #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get f_flags */ diff -ruN linux/include/linux/fs.h linux-iocp/include/linux/fs.h --- linux/include/linux/fs.h Wed Mar 25 19:35:08 1998 +++ linux-iocp/include/linux/fs.h Thu Mar 26 13:27:48 1998 @@ -405,6 +405,8 @@ struct fown_struct f_owner; unsigned long f_version; + struct file *f_notify; /* for i/o completion ports */ + struct file *f_notify_next, *f_notify_prev; /* needed for tty driver, and maybe others */ void *private_data; @@ -727,8 +729,13 @@ extern void put_write_access(struct inode *inode); extern struct dentry * open_namei(const char * pathname, int flag, int mode); extern struct dentry * do_mknod(const char * filename, int mode, dev_t dev); +extern struct inode *get_pipe_inode(void); extern int do_pipe(int *); extern ino_t find_inode_number(struct dentry *, struct qstr *); +extern ssize_t ksys_pread(struct file *file, char *buf, size_t count, + loff_t *pos); +extern ssize_t ksys_pwrite(struct file *file, const char *buf, size_t count, + loff_t *pos); /* * Kernel pointers have redundant information, so we can use a diff -ruN linux/include/linux/iocp.h linux-iocp/include/linux/iocp.h --- linux/include/linux/iocp.h Wed Dec 31 16:00:00 1969 +++ linux-iocp/include/linux/iocp.h Thu Mar 26 13:29:47 1998 @@ -0,0 +1,68 @@ +/* + * I/O completion ports for Linux. + * + * Robey Pointer <robey@lag.net>, 9 march 1998 + */ + +#ifndef _LINUX_IOCP_H +#define _LINUX_IOCP_H + +#define IOCP_MAXQ 15 + +/* queue entries for pending i/o (kernel) */ +struct iocp_io { + struct file *f; + void *buffer; + size_t nbytes; /* bytes transferred */ + loff_t *offset; /* only for pread/pwrite */ + struct task_struct *task; /* which process initiated */ + unsigned int flags; /* see below */ +}; +#define IOCPQ_READ 0x0001 /* read request */ +#define IOCPQ_WRITE 0x0002 /* write request */ +#define IOCPQ_RWMASK 0x0003 +/* internal-use flags for housekeeping: */ +#define IOCPQ_READY 0x0100 /* i/o will probably not block */ + +/* completed i/o struct that will be returned to users */ +struct iocp_io_user { + long fd; + void *buffer; + size_t nbytes; /* bytes requested */ + size_t nresult; /* bytes actually copied */ + loff_t offset; + unsigned int flags; + unsigned int error; +}; + +/* private_data part of an IOCP "file" */ +/* should the size of the queue be user-settable? possible abuse... */ +struct iocp_private { + spinlock_t lock; + int inq_tail; + struct iocp_io inq[IOCP_MAXQ]; /* pending i/o */ + struct file *next; /* linked list of watched files */ +}; + +/* ioctl stuff */ +struct iocp_watch { + unsigned long fd; /* fd to watch */ + int watch; /* on/off */ +}; + +#define IOCPSWATCH _IOW('p', 0xa0, struct iocp_watch) +#define IOCPGWATCH _IOWR('p', 0xa1, struct iocp_watch) + +extern struct file_operations iocp_file_ops; + +int iocp_new(unsigned long fd); +int iocp_addq(struct file *filp, const void *buffer, size_t nbytes, + loff_t *offset, unsigned int flags); +void iocp_put(struct file *filp); +void iocp_notify(struct file *filp, unsigned int flags); +int iocp_io_ready(struct iocp_private *pr); +void iocp_pop(struct iocp_private *pr, int which, struct iocp_io *io); +void iocp_push(struct iocp_private *pr, struct iocp_io *io); + +#endif + diff -ruN linux/net/socket.c linux-iocp/net/socket.c --- linux/net/socket.c Wed Mar 25 19:35:19 1998 +++ linux-iocp/net/socket.c Fri Mar 27 16:07:49 1998 @@ -75,6 +75,7 @@ #include <linux/wanrouter.h> #include <linux/init.h> #include <linux/poll.h> +#include <linux/iocp.h> #if defined(CONFIG_KERNELD) && defined(CONFIG_NET) #include <linux/kerneld.h> @@ -543,7 +544,10 @@ int sock_wake_async(struct socket *sock, int how) { - if (!sock || !sock->fasync_list) + if (!sock) + return -1; + if ((!sock->fasync_list) && + ((!sock->file) || (!sock->file->f_notify))) return -1; switch (how) { @@ -553,6 +557,8 @@ case 1: if (!(sock->flags & SO_WAITDATA)) kill_fasync(sock->fasync_list, SIGIO); + if (sock->file->f_notify) + iocp_notify(sock->file, IOCPQ_READ); break; case 2: if (sock->flags & SO_NOSPACE) @@ -560,6 +566,8 @@ kill_fasync(sock->fasync_list, SIGIO); sock->flags &= ~SO_NOSPACE; } + if (sock->file->f_notify) + iocp_notify(sock->file, IOCPQ_WRITE); break; } return 0;/* test of an iocp-enhanced kernel */
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>
/* sizeof = 8 */ struct iocp_watch { unsigned long fd; int watch; };
#define O_IOCP 040001 #define IOCPSWATCH 0x400870A0 #define IOCPGWATCH 0xC00870A1 #define IOCP_READ 1
struct iocp_io { long fd; void *buffer; unsigned int nbytes; unsigned int nresult; long long offset; unsigned int flags; unsigned int error; };
int get_iocp_result(int iocp, void **buf, int *nbytes) { struct iocp_io compl; int n;
n = read(iocp, &compl, sizeof(compl)); if (n < 0) { perror("iocp read"); exit(1); } if (n != sizeof(compl)) printf("odd: iocp read %d, not %d\n", n, sizeof(compl)); printf(" %s(%d, %08X, %u) = %d\n", compl.flags & IOCP_READ ? "read" : "write", compl.fd, compl.buffer, compl.nbytes, compl.error ? -1 : compl.nresult); if (compl.error) printf(" errno = %u\n", compl.error); *buf = compl.buffer; *nbytes = compl.nresult; return compl.error; }
void main(int argc, char **argv) { int sd, iocp, n; struct iocp_watch iw; struct sockaddr_in sa; char buf[16], *buf2;
/* make an iocp */ iocp = open(NULL, O_IOCP, 0); if (iocp < 0) { perror("open iocp"); exit(1); } /* open a socket */ sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) { perror("socket"); exit(1); } /* bind */ sa.sin_addr.s_addr = 0L; sa.sin_port = 0; sa.sin_family = AF_INET; if (bind(sd, &sa, sizeof(sa)) < 0) { perror("bind"); exit(1); } /* connect to port 80(www) of 204.71.177.71 */ if (inet_aton("204.71.177.71", &(sa.sin_addr)) < 0) { perror("inet_aton"); exit(1); } sa.sin_family = AF_INET; sa.sin_port = htons(80); if (connect(sd, &sa, sizeof(sa)) < 0) { perror("connect"); exit(1); }
if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) { perror("fcntl"); exit(1); }
iw.fd = sd; iw.watch = 9; if (ioctl(iocp, IOCPGWATCH, &iw) < 0) { perror("iocpgwatch"); exit(1); } if (iw.watch) printf("kernel says socket %d is already watched!\n", sd); iw.watch = 1; if (ioctl(iocp, IOCPSWATCH, &iw) < 0) { perror("iocpswatch"); exit(1); } printf("just set socket %d to be watched by iocp %d\n", sd, iocp); if (ioctl(iocp, IOCPGWATCH, &iw) < 0) { perror("iocpgwatch"); exit(1); } printf("kernel says socket %d %s watched by iocp %d\n", sd, iw.watch ? "is" : "isn't", iocp); /* write my http request */ n = write(sd, "GET / HTTP/1.0\r\n\r\n", 18); if (n < 0) perror("socket write via iocp"); if (n >= 0) { printf("somehow, i wrote %d bytes to the async socket.\n", n); exit(1); } /* now try an iocp read */ n = read(sd, buf, 8); if (n < 0) perror("socket read via iocp"); if (n >= 0) { printf("somehow, i read %d bytes from the async socket.\n", n); exit(1); } printf("performing read on iocp to pull in the results.\n"); if (get_iocp_result(iocp, (void **)&buf2, &n) == 0) { printf(" buffer = '%c%c%c%c%c%c%c%c'...\n", buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7]); } if (get_iocp_result(iocp, (void **)&buf2, &n) == 0) { if (buf != buf2) printf("!! buf != compl.buffer\n"); printf(" buffer = '%c%c%c%c%c%c%c%c'\n", buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7]); }
iw.watch = 0; if (ioctl(iocp, IOCPSWATCH, &iw) < 0) { perror("iocpswatch"); exit(1); } iw.watch = 9; if (ioctl(iocp, IOCPGWATCH, &iw) < 0) { perror("iocpgwatch"); exit(1); } if (iw.watch) printf("!! kernel didn't turn off the watch\n");
close(sd); close(iocp); }
| |