lkml.org 
[lkml]   [1998]   [Mar]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectI/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);
}

\
 
 \ /
  Last update: 2005-03-22 13:41    [W:0.126 / U:1.044 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site