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:
Родитель
29bb3158cf
Коммит
ccf7f4088a
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
120
fs/cifs/misc.c
120
fs/cifs/misc.c
|
@ -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_exit:
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче