summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <koverstreet@google.com>2012-10-05 12:22:26 -0700
committerKent Overstreet <koverstreet@google.com>2012-10-05 12:22:26 -0700
commit141df34843d302cc2f1c36009ebc2c3e9e2981e3 (patch)
tree5e6bfb1eedc0b296b83cf9cc186a1ddc5ab286f2
parenta0d271cbfed1dd50278c6b06bead3d00ba0a88f9 (diff)
Extensible AIO interface
-rw-r--r--fs/aio.c115
-rw-r--r--fs/direct-io.c1
-rw-r--r--include/linux/aio.h54
-rw-r--r--include/linux/aio_abi.h41
-rw-r--r--include/linux/blk_types.h3
5 files changed, 212 insertions, 2 deletions
diff --git a/fs/aio.c b/fs/aio.c
index 71f613cf4a85..816f419f9747 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -424,6 +424,8 @@ static struct kiocb *__aio_get_req(struct kioctx *ctx)
req->private = NULL;
req->ki_iovec = NULL;
INIT_LIST_HEAD(&req->ki_run_list);
+ req->ki_attrs = NULL;
+ req->ki_attr_rets = NULL;
req->ki_eventfd = NULL;
return req;
@@ -538,6 +540,7 @@ static inline void really_put_req(struct kioctx *ctx, struct kiocb *req)
req->ki_dtor(req);
if (req->ki_iovec != &req->ki_inline_vec)
kfree(req->ki_iovec);
+ kfree(req->ki_attrs);
kmem_cache_free(kiocb_cachep, req);
ctx->reqs_active--;
@@ -1504,6 +1507,109 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat)
return 0;
}
+static const size_t iocb_attr_sizes[] = {
+ [IOCB_ATTR_proxy_pid] = sizeof(struct iocb_attr_proxy_pid),
+};
+
+static const size_t iocb_attr_ret_sizes[] = {
+ [IOCB_ATTR_proxy_pid] = sizeof(struct iocb_attr_ret_proxy_pid),
+};
+
+static int aio_setup_attrs(struct iocb __user *user_iocb,
+ struct iocb *iocb, struct kiocb *req)
+{
+ struct iocb_attr_list __user *user_attrs;
+ struct iocb_attr_ret_list __user *user_attr_rets;
+ struct iocb_attr *attr, *end;
+ struct iocb_attr_ret *attr_ret;
+ u64 size, ret_size = sizeof(struct iocb_attr_ret_list);
+ int ret;
+
+ user_attrs = (void *) &user_iocb[1];
+ user_attr_rets = (void *) iocb->aio_attr_rets;
+
+ if (!(iocb->aio_flags & IOCB_FLAG_ATTR))
+ return 0;
+
+ if (unlikely(get_user(size, &user_attrs->size)))
+ return -EFAULT;
+
+ if (size == sizeof(struct iocb_attr_list)) {
+ /*
+ * We were passed an empty attribute list, so we can skip the
+ * rest of the setup but we still need to return an empty
+ * iocb_attr_ret_list:
+ */
+
+ struct iocb_attr_ret_list tmp;
+ tmp.size = sizeof(tmp);
+
+ if (copy_to_user(user_attr_rets, &tmp, sizeof(tmp)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ if (unlikely(size > PAGE_SIZE))
+ return -EFAULT;
+
+ req->ki_attrs = kmalloc(size, GFP_KERNEL);
+ if (unlikely(!req->ki_attrs))
+ return -ENOMEM;
+
+ req->ki_attrs->size = size;
+
+ ret = -EFAULT;
+ if (unlikely(copy_from_user(req->ki_attrs->attrs,
+ user_attrs->attrs,
+ size - sizeof(u64))))
+ goto err_free;
+
+ end = ((void *) req->ki_attrs) + req->ki_attrs->size;
+
+ ret = -EINVAL;
+ for_each_iocb_attr(attr, req->ki_attrs) {
+ if (attr > end)
+ goto err_free;
+
+ if (attr->size < sizeof(struct iocb_attr))
+ goto err_free;
+
+ if (attr->id >= IOCB_ATTR_MAX)
+ goto err_free;
+
+ if (attr->size != iocb_attr_sizes[attr->id])
+ goto err_free;
+
+ ret_size += iocb_attr_ret_sizes[attr->id];
+ }
+
+ ret = -ENOMEM;
+ req->ki_attr_rets = kmalloc(ret_size, GFP_KERNEL);
+ if (!req->ki_attr_rets)
+ goto err_free;
+
+ req->ki_attr_rets->size = ret_size;
+
+ attr_ret = req->ki_attr_rets->rets;
+
+ for_each_iocb_attr(attr, req->ki_attrs) {
+ attr_ret->cookie = attr->cookie;
+ attr_ret->size = iocb_attr_ret_sizes[attr->id];
+ attr_ret->ret = -EINVAL;
+
+ attr->cookie = (unsigned long) attr_ret;
+
+ attr_ret = ((void *) attr_ret) + attr_ret->size;
+ }
+
+ return 0;
+err_free:
+ kfree(req->ki_attrs);
+ req->ki_attrs = NULL;
+ return ret;
+}
+
static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
struct iocb *iocb, struct kiocb_batch *batch,
bool compat)
@@ -1513,7 +1619,9 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
ssize_t ret;
/* enforce forwards compatibility on users */
- if (unlikely(iocb->aio_reserved1 || iocb->aio_reserved2)) {
+ if (unlikely(iocb->aio_reserved1 ||
+ (iocb->aio_attr_rets &&
+ !(iocb->aio_flags & IOCB_FLAG_ATTR)))) {
pr_debug("EINVAL: io_submit: reserve field set\n");
return -EINVAL;
}
@@ -1538,6 +1646,11 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
return -EAGAIN;
}
req->ki_filp = file;
+
+ ret = aio_setup_attrs(user_iocb, iocb, req);
+ if (ret)
+ goto out_put_req;
+
if (iocb->aio_flags & IOCB_FLAG_RESFD) {
/*
* If the IOCB_FLAG_RESFD flag of aio_flags is set, get an
diff --git a/fs/direct-io.c b/fs/direct-io.c
index f86c720dba0e..f58f44fc2f1d 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -371,6 +371,7 @@ static inline void dio_bio_submit(struct dio *dio, struct dio_submit *sdio)
unsigned long flags;
bio->bi_private = dio;
+ bio->bi_attrs = dio->iocb->ki_attrs;
spin_lock_irqsave(&dio->bio_lock, flags);
dio->refcount++;
diff --git a/include/linux/aio.h b/include/linux/aio.h
index 31ff6dba4872..78af6199812b 100644
--- a/include/linux/aio.h
+++ b/include/linux/aio.h
@@ -119,6 +119,9 @@ struct kiocb {
* for cancellation */
struct list_head ki_batch; /* batch allocation */
+ struct iocb_attr_list *ki_attrs;
+ struct iocb_attr_ret_list *ki_attr_rets;
+
/*
* If the aio_resfd field of the userspace iocb is not zero,
* this is the underlying eventfd context to deliver events to.
@@ -230,11 +233,62 @@ static inline long do_io_submit(aio_context_t ctx_id, long nr,
bool compat) { return 0; }
#endif /* CONFIG_AIO */
+#define for_each_iocb_attr(attr, attr_list) \
+ for (attr = (attr_list)->attrs; \
+ attr != ((void *) (attr_list)) + (attr_list)->size; \
+ attr = ((void *) attr) + (attr)->size)
+
static inline struct kiocb *list_kiocb(struct list_head *h)
{
return list_entry(h, struct kiocb, ki_list);
}
+static inline struct iocb_attr *iocb_attr_next(struct iocb_attr_list *attrs,
+ struct iocb_attr *attr)
+{
+ void *end = ((void *) attrs) + attrs->size;
+
+ if (attr)
+ attr = ((void *) attr) + attr->size;
+ else
+ attr = attrs->attrs;
+
+ if (attr == end)
+ return attr;
+
+ return attr;
+}
+
+static inline void *__iocb_attr_lookup(struct iocb_attr_list *attrs, unsigned id)
+{
+ struct iocb_attr *attr = NULL;
+
+ if (!attrs)
+ return NULL;
+
+ while (1) {
+ attr = iocb_attr_next(attrs, attr);
+ if (!attr)
+ return NULL;
+
+ if (attr->id == id)
+ return attr;
+ }
+
+ return NULL;
+}
+
+#define iocb_attr_lookup(attrs, id) \
+({ \
+ struct iocb_attr *_attr; \
+ \
+ _attr = __iocb_attr_lookup((attrs), IOCB_ATTR_ ## id); \
+ if (_attr->size != sizeof(struct iocb_attr_ ## id)) \
+ _attr = NULL; \
+ \
+ (struct iocb_attr_ ## id *) _attr; \
+})
+
/* for sysctl: */
extern unsigned long aio_nr;
extern unsigned long aio_max_nr;
diff --git a/include/linux/aio_abi.h b/include/linux/aio_abi.h
index 86fa7a71336a..f86c862dfba3 100644
--- a/include/linux/aio_abi.h
+++ b/include/linux/aio_abi.h
@@ -53,6 +53,7 @@ enum {
* is valid.
*/
#define IOCB_FLAG_RESFD (1 << 0)
+#define IOCB_FLAG_ATTR (1 << 1)
/* read() from /dev/aio returns these structures. */
struct io_event {
@@ -92,7 +93,7 @@ struct iocb {
__s64 aio_offset;
/* extra parameters */
- __u64 aio_reserved2; /* TODO: use this for a (struct sigevent *) */
+ __u64 aio_attr_rets;
/* flags for the "struct iocb" */
__u32 aio_flags;
@@ -104,6 +105,44 @@ struct iocb {
__u32 aio_resfd;
}; /* 64 bytes */
+struct iocb_attr {
+ __u64 cookie;
+ __u32 size;
+ __u32 id;
+ __u8 data[0];
+} __aligned(8);
+
+struct iocb_attr_list {
+ __u64 size;
+ struct iocb_attr attrs[];
+};
+
+struct iocb_attr_ret {
+ __u64 cookie;
+ __u32 size;
+ __u32 ret;
+ __u8 data[0];
+} __aligned(8);
+
+struct iocb_attr_ret_list {
+ __u64 size;
+ struct iocb_attr_ret rets[];
+};
+
+enum {
+ IOCB_ATTR_proxy_pid,
+ IOCB_ATTR_MAX
+};
+
+struct iocb_attr_proxy_pid {
+ struct iocb_attr attr;
+ __u64 pid;
+};
+
+struct iocb_attr_ret_proxy_pid {
+ struct iocb_attr_ret attr;
+};
+
#undef IFBIG
#undef IFLITTLE
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 7b7ac9ccec7a..e2e37f783cbc 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -68,6 +68,9 @@ struct bio {
bio_end_io_t *bi_end_io;
void *bi_private;
+
+ struct iocb_attr_list *bi_attrs;
+
#ifdef CONFIG_BLK_CGROUP
/*
* Optional ioc and css associated with this bio. Put on bio