aioevent: This patch adds POSIX AIO completion notification event by adding an aio_sigevent field to the aiocb. The sigevent structure is filled in by the user application as part of the AIO request preparation. Upon request completion, the kernel notifies the application using those sigevent parameters. Makefile | 2 fs/aio.c | 264 ++++++++++++++++++++++++++++++++++++++++-------- include/linux/aio.h | 10 + include/linux/aio_abi.h | 2 4 files changed, 236 insertions(+), 42 deletions(-) Signed-off-by: Sébastien Dugué Index: linux-2.6.12/fs/aio.c =================================================================== --- linux-2.6.12.orig/fs/aio.c 2005-06-23 14:19:26.000000000 +0200 +++ linux-2.6.12/fs/aio.c 2005-06-23 14:19:28.000000000 +0200 @@ -406,6 +406,10 @@ req->ki_cancel = NULL; req->ki_retry = NULL; req->ki_dtor = NULL; + req->ki_pid = 0; + req->ki_signo = 0; + req->ki_notify = 0; + req->ki_value = 0; req->private = NULL; INIT_LIST_HEAD(&req->ki_run_list); @@ -906,6 +910,92 @@ } EXPORT_SYMBOL(kick_iocb); +static void __aio_send_signal(pid_t pid, int signo, int notify, void *obj) +{ + struct siginfo info; + struct task_struct *p; + + memset(&info, 0, sizeof(struct siginfo)); + + info.si_signo = signo; + info.si_errno = 0; + info.si_code = SI_ASYNCIO; + info.si_pid = 0; + info.si_uid = 0; + info.si_ptr = obj; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + + if (p->sighand) { + unsigned long flags; + int ret = -1; + + spin_lock_irqsave(&p->sighand->siglock, flags); + + switch(notify) { + + case IO_NOTIFY_SIGNAL: + ret = __group_send_sig_info(signo, &info, p); + break; + case IO_NOTIFY_THREAD_ID: + //ret = specific_send_sig_info(signo, &info, p); + ret = __group_send_sig_info(signo, &info, p); + break; + } + + spin_unlock_irqrestore(&p->sighand->siglock, flags); + + if (ret) + printk(KERN_DEBUG "__aio_send_signal: failed to send signal %d to %d\n", + signo, pid); + } + read_unlock(&tasklist_lock); +} + +static void __aio_write_evt(struct kioctx *ctx, struct io_event *event) +{ + struct aio_ring_info *info; + struct aio_ring *ring; + struct io_event *ring_event; + unsigned long tail; + + info = &ctx->ring_info; + + /* add a completion event to the ring buffer. + * must be done holding ctx->ctx_lock to prevent + * other code from messing with the tail + * pointer since we might be called from irq + * context. + */ + + ring = kmap_atomic(info->ring_pages[0], KM_IRQ1); + + tail = info->tail; + ring_event = aio_ring_event(info, tail, KM_IRQ0); + if (++tail >= info->nr) + tail = 0; + + *ring_event = *event; + + dprintk("aio_write_evt: %p[%lu]: %Lx %Lx %Lx %Lx\n", + ctx, tail, event->obj, event->data, event->res, event->res2); + + /* after flagging the request as done, we + * must never even look at it again + */ + + smp_wmb(); /* make event visible before updating tail */ + + info->tail = tail; + ring->tail = tail; + + put_aio_ring_event(ring_event, KM_IRQ0); + kunmap_atomic(ring, KM_IRQ1); + + pr_debug("added to ring at [%lu]\n", tail); +} + /* aio_complete * Called when the io request on the given iocb is complete. * Returns true if this is the last user of the request. The @@ -914,11 +1004,8 @@ int fastcall aio_complete(struct kiocb *iocb, long res, long res2) { struct kioctx *ctx = iocb->ki_ctx; - struct aio_ring_info *info; - struct aio_ring *ring; - struct io_event *event; + struct io_event event; unsigned long flags; - unsigned long tail; int ret; /* Special case handling for sync iocbs: events go directly @@ -945,14 +1032,13 @@ return ret; } - info = &ctx->ring_info; + /* insert event in the event ring */ + + event.obj = (u64)(unsigned long)iocb->ki_obj.user; + event.data = iocb->ki_user_data; + event.res = res; + event.res2 = res2; - /* add a completion event to the ring buffer. - * must be done holding ctx->ctx_lock to prevent - * other code from messing with the tail - * pointer since we might be called from irq - * context. - */ spin_lock_irqsave(&ctx->ctx_lock, flags); if (iocb->ki_run_list.prev && !list_empty(&iocb->ki_run_list)) @@ -965,34 +1051,11 @@ if (kiocbIsCancelled(iocb)) goto put_rq; - ring = kmap_atomic(info->ring_pages[0], KM_IRQ1); - - tail = info->tail; - event = aio_ring_event(info, tail, KM_IRQ0); - if (++tail >= info->nr) - tail = 0; + __aio_write_evt(ctx, &event); - event->obj = (u64)(unsigned long)iocb->ki_obj.user; - event->data = iocb->ki_user_data; - event->res = res; - event->res2 = res2; - - dprintk("aio_complete: %p[%lu]: %p: %p %Lx %lx %lx\n", - ctx, tail, iocb, iocb->ki_obj.user, iocb->ki_user_data, - res, res2); - - /* after flagging the request as done, we - * must never even look at it again - */ - smp_wmb(); /* make event visible before updating tail */ - - info->tail = tail; - ring->tail = tail; - - put_aio_ring_event(event, KM_IRQ0); - kunmap_atomic(ring, KM_IRQ1); - - pr_debug("added to ring %p at [%lu]\n", iocb, tail); + if (iocb->ki_signo) + __aio_send_signal(iocb->ki_pid, iocb->ki_signo, iocb->ki_notify, + (void*)(unsigned long)iocb->ki_value); pr_debug("%ld retries: %d of %d\n", iocb->ki_retried, iocb->ki_nbytes - iocb->ki_left, iocb->ki_nbytes); @@ -1470,10 +1533,14 @@ struct kiocb *req; struct file *file; ssize_t ret; + int notify = 0; + struct sigevent __user *user_event = NULL; + int aio_pid = 0; + int aio_signo = 0; + int aio_value = 0; /* enforce forwards compatibility on users */ - if (unlikely(iocb->aio_reserved1 || iocb->aio_reserved2 || - iocb->aio_reserved3)) { + if (unlikely(iocb->aio_reserved1)) { pr_debug("EINVAL: io_submit: reserve field set\n"); return -EINVAL; } @@ -1488,6 +1555,74 @@ return -EINVAL; } + + /* Get sigevent */ + if (unlikely(__get_user(user_event, (struct sigevent __user **)&user_iocb->aio_sigevent))) { + return -EFAULT; + } + + if (user_event != NULL) { + + /* + * We avoid copying the whole sigevent bunch and only get the + * needed fields. + */ + if (unlikely(__get_user(aio_pid, + &user_event->sigev_notify_thread_id))) { + return -EFAULT; + } + + if (unlikely(__get_user(aio_signo, + &user_event->sigev_signo))) { + return -EFAULT; + } + + if (unlikely(__get_user(aio_value, + &user_event->sigev_value.sival_int))) { + return -EFAULT; + } + } + + if (aio_signo) { + + if ( (iocb->aio_lio_opcode != IOCB_CMD_PREAD) && + (iocb->aio_lio_opcode != IOCB_CMD_PWRITE) ) + return -EINVAL; + + if (aio_pid == 0) { + /* notify itself */ + + aio_pid = current->pid; + notify = IO_NOTIFY_SIGNAL; + } else { + pid_t group_id; + task_t *ptask; + /* notify given thread */ + + /* caller thread and target thread must be in same + * thread group + */ + + read_lock(&tasklist_lock); + + ptask = find_task_by_pid(aio_pid); + + if (unlikely (ptask == NULL)) { + read_unlock(&tasklist_lock); + return -EFAULT; + } + + group_id = ptask->tgid; + + read_unlock(&tasklist_lock); + + if (group_id != current->tgid) + return -EINVAL; + + notify = IO_NOTIFY_THREAD_ID; + } + } + file = fget(iocb->aio_fildes); if (unlikely(!file)) return -EBADF; @@ -1509,6 +1644,13 @@ req->ki_user_data = iocb->aio_data; req->ki_pos = iocb->aio_offset; + if (aio_signo) { + req->ki_pid = aio_pid; + req->ki_signo = aio_signo; + req->ki_notify = notify; + req->ki_value = aio_value; + } + req->ki_buf = (char __user *)(unsigned long)iocb->aio_buf; req->ki_left = req->ki_nbytes = iocb->aio_nbytes; req->ki_opcode = iocb->aio_lio_opcode; @@ -1578,6 +1720,10 @@ for (i=0; iaio_sigevent))) { + ret = -EFAULT; + break; + } + + if (user_event != NULL) { + + /* Check user_event access */ + if (unlikely(!access_ok(VERIFY_READ, user_event, + sizeof(struct sigevent)))) { + ret = -EFAULT; + break; + } + + /* + * We avoid copying the whole sigevent bunch and only get the + * needed fields. + */ + if (unlikely(__get_user(aio_pid, + &user_event->sigev_notify_thread_id))) { + ret = -EFAULT; + break; + } + + if (unlikely(__get_user(aio_signo, + &user_event->sigev_signo))) { + ret = -EFAULT; + break; + } + } + ret = io_submit_one(ctx, user_iocb, &tmp); if (ret) break; Index: linux-2.6.12/include/linux/aio_abi.h =================================================================== --- linux-2.6.12.orig/include/linux/aio_abi.h 2005-06-23 14:19:20.000000000 +0200 +++ linux-2.6.12/include/linux/aio_abi.h 2005-06-23 14:19:28.000000000 +0200 @@ -81,7 +81,7 @@ __s64 aio_offset; /* extra parameters */ - __u64 aio_reserved2; /* TODO: use this for a (struct sigevent *) */ + __u64 aio_sigevent; /* TODO: use this for a (struct sigevent *) */ __u64 aio_reserved3; }; /* 64 bytes */ Index: linux-2.6.12/include/linux/aio.h =================================================================== --- linux-2.6.12.orig/include/linux/aio.h 2005-06-23 14:19:26.000000000 +0200 +++ linux-2.6.12/include/linux/aio.h 2005-06-23 14:19:28.000000000 +0200 @@ -7,6 +7,11 @@ #include +enum { + IO_NOTIFY_SIGNAL = 0, /* send signal to a processe */ + IO_NOTIFY_THREAD_ID = 1, /* send signal to a specific thread */ +}; + #define AIO_MAXSEGS 4 #define AIO_KIOGRP_NR_ATOMIC 8 @@ -73,6 +78,11 @@ long ki_retried; /* just for testing */ long ki_kicked; /* just for testing */ long ki_queued; /* just for testing */ + /* to notify a process on I/O event */ + __s32 ki_pid; + __u16 ki_signo; + __u16 ki_notify; + __u64 ki_value; void *private; }; Index: linux-2.6.12/Makefile =================================================================== --- linux-2.6.12.orig/Makefile 2005-06-23 14:19:26.000000000 +0200 +++ linux-2.6.12/Makefile 2005-06-23 14:19:28.000000000 +0200 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 12 -EXTRAVERSION = .PAIO-aiomaxevents +EXTRAVERSION = .PAIO-aioevent NAME=Woozy Numbat # *DOCUMENTATION*