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.075 / U:118.184 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site