CIFS: Add asynchronous context to support kernel AIO

Currently the code doesn't recognize asynchronous calls passed
by io_submit() and processes all calls synchronously. This is not
what kernel AIO expects. This patch introduces a new async context
that keeps track of all issued i/o requests and moves a response
collecting procedure to a separate thread. This allows to return
to a caller immediately for async calls and call iocb->ki_complete()
once all requests are completed. For sync calls the current thread
simply waits until all requests are completed.

Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
Pavel Shilovsky 2017-04-25 11:52:29 -07:00 committed by Steve French
parent 29bb3158cf
commit ccf7f4088a
3 changed files with 139 additions and 0 deletions

View File

@ -1115,6 +1115,22 @@ struct cifs_io_parms {
struct cifs_tcon *tcon;
};
struct cifs_aio_ctx {
struct kref refcount;
struct list_head list;
struct mutex aio_mutex;
struct completion done;
struct iov_iter iter;
struct kiocb *iocb;
struct cifsFileInfo *cfile;
struct bio_vec *bv;
unsigned int npages;
ssize_t rc;
unsigned int len;
unsigned int total_len;
bool should_dirty;
};
struct cifs_readdata;
/* asynchronous read support */

View File

@ -535,4 +535,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
struct shash_desc *shash);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
enum securityEnum);
struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
void cifs_aio_ctx_release(struct kref *refcount);
int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
#endif /* _CIFSPROTO_H */

View File

@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/mempool.h>
#include <linux/vmalloc.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
@ -741,3 +742,122 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
}
return rc;
}
struct cifs_aio_ctx *
cifs_aio_ctx_alloc(void)
{
struct cifs_aio_ctx *ctx;
ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL);
if (!ctx)
return NULL;
INIT_LIST_HEAD(&ctx->list);
mutex_init(&ctx->aio_mutex);
init_completion(&ctx->done);
kref_init(&ctx->refcount);
return ctx;
}
void
cifs_aio_ctx_release(struct kref *refcount)
{
struct cifs_aio_ctx *ctx = container_of(refcount,
struct cifs_aio_ctx, refcount);
cifsFileInfo_put(ctx->cfile);
kvfree(ctx->bv);
kfree(ctx);
}
#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024)
int
setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw)
{
ssize_t rc;
unsigned int cur_npages;
unsigned int npages = 0;
unsigned int i;
size_t len;
size_t count = iov_iter_count(iter);
unsigned int saved_len;
size_t start;
unsigned int max_pages = iov_iter_npages(iter, INT_MAX);
struct page **pages = NULL;
struct bio_vec *bv = NULL;
if (iter->type & ITER_KVEC) {
memcpy(&ctx->iter, iter, sizeof(struct iov_iter));
ctx->len = count;
iov_iter_advance(iter, count);
return 0;
}
if (max_pages * sizeof(struct bio_vec) <= CIFS_AIO_KMALLOC_LIMIT)
bv = kmalloc_array(max_pages, sizeof(struct bio_vec),
GFP_KERNEL);
if (!bv) {
bv = vmalloc(max_pages * sizeof(struct bio_vec));
if (!bv)
return -ENOMEM;
}
if (max_pages * sizeof(struct page *) <= CIFS_AIO_KMALLOC_LIMIT)
pages = kmalloc_array(max_pages, sizeof(struct page *),
GFP_KERNEL);
if (!pages) {
pages = vmalloc(max_pages * sizeof(struct page *));
if (!bv) {
kvfree(bv);
return -ENOMEM;
}
}
saved_len = count;
while (count && npages < max_pages) {
rc = iov_iter_get_pages(iter, pages, count, max_pages, &start);
if (rc < 0) {
cifs_dbg(VFS, "couldn't get user pages (rc=%zd)\n", rc);
break;
}
if (rc > count) {
cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc,
count);
break;
}
iov_iter_advance(iter, rc);
count -= rc;
rc += start;
cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE);
if (npages + cur_npages > max_pages) {
cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n",
npages + cur_npages, max_pages);
break;
}
for (i = 0; i < cur_npages; i++) {
len = rc > PAGE_SIZE ? PAGE_SIZE : rc;
bv[npages + i].bv_page = pages[i];
bv[npages + i].bv_offset = start;
bv[npages + i].bv_len = len - start;
rc -= len;
start = 0;
}
npages += cur_npages;
}
kvfree(pages);
ctx->bv = bv;
ctx->len = saved_len - count;
ctx->npages = npages;
iov_iter_bvec(&ctx->iter, ITER_BVEC | rw, ctx->bv, npages, ctx->len);
return 0;
}