cancelfd: This patch adds support for POSIX AIO cancelation against a file descriptor. Makefile | 2 fs/aio.c | 141 +++++++++++++++++++++++++++++++++++++----------- include/linux/aio_abi.h | 6 ++ 3 files changed, 118 insertions(+), 31 deletions(-) Signed-off-by: Sébastien Dugué Index: linux-2.6.12/fs/aio.c =================================================================== --- linux-2.6.12.orig/fs/aio.c 2005-07-21 11:43:26.000000000 +0200 +++ linux-2.6.12/fs/aio.c 2005-07-21 11:43:27.000000000 +0200 @@ -263,6 +263,28 @@ return ctx; } +static int __aio_cancel(struct kiocb *kiocb, struct io_event *result) +{ + int (*cancel)(struct kiocb *iocb, struct io_event *res); + + if (kiocb == NULL) + return -EFAULT; + + cancel = kiocb->ki_cancel; + if (NULL == cancel) { + printk(KERN_DEBUG "iocb has no cancel operation\n"); + return -EFAULT; + } + + kiocb->ki_users ++; + + memset(result, 0, sizeof(*result)); + result->obj = (u64)(unsigned long)kiocb->ki_obj.user; + result->data = kiocb->ki_user_data; + + return cancel(kiocb, result); +} + /* aio_cancel_all * Cancels all outstanding aio requests on an aio context. Used * when the processes owning a context have all exited to encourage @@ -270,7 +292,6 @@ */ static void aio_cancel_all(struct kioctx *ctx) { - int (*cancel)(struct kiocb *, struct io_event *); struct io_event res; spin_lock_irq(&ctx->ctx_lock); ctx->dead = 1; @@ -278,14 +299,10 @@ struct list_head *pos = ctx->active_reqs.next; struct kiocb *iocb = list_kiocb(pos); list_del_init(&iocb->ki_list); - cancel = iocb->ki_cancel; kiocbSetCancelled(iocb); - if (cancel) { - iocb->ki_users++; - spin_unlock_irq(&ctx->ctx_lock); - cancel(iocb, &res); - spin_lock_irq(&ctx->ctx_lock); - } + spin_unlock_irq(&ctx->ctx_lock); + __aio_cancel(iocb, &res); + spin_lock_irq(&ctx->ctx_lock); } spin_unlock_irq(&ctx->ctx_lock); } @@ -2028,6 +2045,67 @@ return NULL; } +static int aio_cancel_fd(aio_context_t ctx_id, int fildes) +{ + struct kioctx *ctx; + struct kiocb *kiocb; + int ret; + struct file *file; + struct list_head *pos; + int all_done; + int not_canceled; + struct io_event result_event; + + /* cancel all I/O belonging to filedes */ + + file = fget(fildes); + if (unlikely(!file)) + return -EBADF; + + /* we must exit on EBADF before EINVAL */ + + ctx = lookup_ioctx(ctx_id); + if (unlikely(!ctx)) { + return -EINVAL; + } + + /* get all operation for this file descriptor */ + + all_done = 1; + not_canceled = 0; + spin_lock_irq(&ctx->ctx_lock); + + list_for_each(pos, &ctx->active_reqs) { + + kiocb = list_kiocb(pos); + + if (kiocb->ki_filp == file) { + + all_done = 0; + + spin_unlock_irq(&ctx->ctx_lock); + ret = __aio_cancel(kiocb, &result_event); + spin_lock_irq(&ctx->ctx_lock); + if (ret) + not_canceled++; + else + __aio_write_evt(kiocb->ki_ctx, &result_event); + } + } + + spin_unlock_irq(&ctx->ctx_lock); + + put_ioctx(ctx); + + if (all_done) + return AIO_ALLDONE; + + if (not_canceled) + return AIO_NOTCANCELED; + + return AIO_CANCELED; +} + /* sys_io_cancel: * Attempts to cancel an iocb previously passed to io_submit. If * the operation is successfully cancelled, the resulting event is @@ -2041,9 +2119,9 @@ asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb, struct io_event __user *result) { - int (*cancel)(struct kiocb *iocb, struct io_event *res); struct kioctx *ctx; struct kiocb *kiocb; + struct io_event tmp; u32 key; int ret; @@ -2051,37 +2129,40 @@ if (unlikely(ret)) return -EFAULT; + if ( (key == 0) && (result == NULL) ) { + /* cancel all IOCBs belonging to iocb->aio_fildes */ + + ret = aio_cancel_fd(ctx_id, iocb->aio_fildes); + + return ret; + } + ctx = lookup_ioctx(ctx_id); if (unlikely(!ctx)) return -EINVAL; spin_lock_irq(&ctx->ctx_lock); - ret = -EAGAIN; kiocb = lookup_kiocb(ctx, iocb, key); - if (kiocb && kiocb->ki_cancel) { - cancel = kiocb->ki_cancel; - kiocb->ki_users ++; - kiocbSetCancelled(kiocb); - } else - cancel = NULL; spin_unlock_irq(&ctx->ctx_lock); - if (NULL != cancel) { - struct io_event tmp; - pr_debug("calling cancel\n"); - memset(&tmp, 0, sizeof(tmp)); - tmp.obj = (u64)(unsigned long)kiocb->ki_obj.user; - tmp.data = kiocb->ki_user_data; - ret = cancel(kiocb, &tmp); - if (!ret) { - /* Cancellation succeeded -- copy the result - * into the user's buffer. - */ + if (kiocb == NULL) { + put_ioctx(ctx); + return -EINVAL; + } + + ret = __aio_cancel(kiocb, &tmp); + if (!ret) { + /* Cancellation succeeded -- copy the result + * into the user's buffer. + */ + if (result == NULL) { + spin_lock_irq(&ctx->ctx_lock); + __aio_write_evt(kiocb->ki_ctx, &tmp); + spin_unlock_irq(&ctx->ctx_lock); + } else if (copy_to_user(result, &tmp, sizeof(tmp))) ret = -EFAULT; - } - } else - printk(KERN_DEBUG "iocb has no cancel operation\n"); + } put_ioctx(ctx); Index: linux-2.6.12/include/linux/aio_abi.h =================================================================== --- linux-2.6.12.orig/include/linux/aio_abi.h 2005-07-21 11:43:26.000000000 +0200 +++ linux-2.6.12/include/linux/aio_abi.h 2005-07-21 11:43:27.000000000 +0200 @@ -45,6 +45,12 @@ IOCB_CMD_CHECKPOINT = 8, }; +enum { + AIO_CANCELED = 0, + AIO_NOTCANCELED = 1, + AIO_ALLDONE = 2, +}; + /* read() from /dev/aio returns these structures. */ struct io_event { __u64 data; /* the data field from the iocb */ Index: linux-2.6.12/Makefile =================================================================== --- linux-2.6.12.orig/Makefile 2005-07-21 11:43:26.000000000 +0200 +++ linux-2.6.12/Makefile 2005-07-21 11:43:27.000000000 +0200 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 12 -EXTRAVERSION = .PAIO-liowait +EXTRAVERSION = .PAIO NAME=Woozy Numbat # *DOCUMENTATION*