NFS: Replace atomic_t variables in nfs_direct_req with a single spin lock
Three atomic_t variables cause a lot of bus locking. Because they are all used in the same places in the code, just use a single spin lock. Now that the atomic_t variables are gone, we can remove the request size limitation since the code no longer depends on the limited width of atomic_t on some platforms. Test plan: Compile with CONFIG_NFS and CONFIG_NFS_DIRECTIO enabled. Millions of fsx operations, iozone, OraSim. Signed-off-by: Chuck Lever <cel@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
88467055f7
commit
15ce4a0c1c
@ -58,7 +58,6 @@
|
|||||||
#include "iostat.h"
|
#include "iostat.h"
|
||||||
|
|
||||||
#define NFSDBG_FACILITY NFSDBG_VFS
|
#define NFSDBG_FACILITY NFSDBG_VFS
|
||||||
#define MAX_DIRECTIO_SIZE (4096UL << PAGE_SHIFT)
|
|
||||||
|
|
||||||
static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty);
|
static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty);
|
||||||
static kmem_cache_t *nfs_direct_cachep;
|
static kmem_cache_t *nfs_direct_cachep;
|
||||||
@ -68,6 +67,8 @@ static kmem_cache_t *nfs_direct_cachep;
|
|||||||
*/
|
*/
|
||||||
struct nfs_direct_req {
|
struct nfs_direct_req {
|
||||||
struct kref kref; /* release manager */
|
struct kref kref; /* release manager */
|
||||||
|
|
||||||
|
/* I/O parameters */
|
||||||
struct list_head list; /* nfs_read/write_data structs */
|
struct list_head list; /* nfs_read/write_data structs */
|
||||||
struct file * filp; /* file descriptor */
|
struct file * filp; /* file descriptor */
|
||||||
struct kiocb * iocb; /* controlling i/o request */
|
struct kiocb * iocb; /* controlling i/o request */
|
||||||
@ -75,12 +76,14 @@ struct nfs_direct_req {
|
|||||||
struct inode * inode; /* target file of i/o */
|
struct inode * inode; /* target file of i/o */
|
||||||
struct page ** pages; /* pages in our buffer */
|
struct page ** pages; /* pages in our buffer */
|
||||||
unsigned int npages; /* count of pages */
|
unsigned int npages; /* count of pages */
|
||||||
atomic_t complete, /* i/os we're waiting for */
|
|
||||||
count, /* bytes actually processed */
|
/* completion state */
|
||||||
|
spinlock_t lock; /* protect completion state */
|
||||||
|
int outstanding; /* i/os we're waiting for */
|
||||||
|
ssize_t count, /* bytes actually processed */
|
||||||
error; /* any reported error */
|
error; /* any reported error */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nfs_direct_IO - NFS address space operation for direct I/O
|
* nfs_direct_IO - NFS address space operation for direct I/O
|
||||||
* @rw: direction (read or write)
|
* @rw: direction (read or write)
|
||||||
@ -110,12 +113,6 @@ static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t siz
|
|||||||
unsigned long page_count;
|
unsigned long page_count;
|
||||||
size_t array_size;
|
size_t array_size;
|
||||||
|
|
||||||
/* set an arbitrary limit to prevent type overflow */
|
|
||||||
if (size > MAX_DIRECTIO_SIZE) {
|
|
||||||
*pages = NULL;
|
|
||||||
return -EFBIG;
|
|
||||||
}
|
|
||||||
|
|
||||||
page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||||
page_count -= user_addr >> PAGE_SHIFT;
|
page_count -= user_addr >> PAGE_SHIFT;
|
||||||
|
|
||||||
@ -164,8 +161,10 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
|
|||||||
init_waitqueue_head(&dreq->wait);
|
init_waitqueue_head(&dreq->wait);
|
||||||
INIT_LIST_HEAD(&dreq->list);
|
INIT_LIST_HEAD(&dreq->list);
|
||||||
dreq->iocb = NULL;
|
dreq->iocb = NULL;
|
||||||
atomic_set(&dreq->count, 0);
|
spin_lock_init(&dreq->lock);
|
||||||
atomic_set(&dreq->error, 0);
|
dreq->outstanding = 0;
|
||||||
|
dreq->count = 0;
|
||||||
|
dreq->error = 0;
|
||||||
|
|
||||||
return dreq;
|
return dreq;
|
||||||
}
|
}
|
||||||
@ -181,19 +180,18 @@ static void nfs_direct_req_release(struct kref *kref)
|
|||||||
*/
|
*/
|
||||||
static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq)
|
static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq)
|
||||||
{
|
{
|
||||||
int result = -EIOCBQUEUED;
|
ssize_t result = -EIOCBQUEUED;
|
||||||
|
|
||||||
/* Async requests don't wait here */
|
/* Async requests don't wait here */
|
||||||
if (dreq->iocb)
|
if (dreq->iocb)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
result = wait_event_interruptible(dreq->wait,
|
result = wait_event_interruptible(dreq->wait, (dreq->outstanding == 0));
|
||||||
(atomic_read(&dreq->complete) == 0));
|
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
result = atomic_read(&dreq->error);
|
result = dreq->error;
|
||||||
if (!result)
|
if (!result)
|
||||||
result = atomic_read(&dreq->count);
|
result = dreq->count;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
kref_put(&dreq->kref, nfs_direct_req_release);
|
kref_put(&dreq->kref, nfs_direct_req_release);
|
||||||
@ -214,9 +212,9 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
|
|||||||
nfs_free_user_pages(dreq->pages, dreq->npages, 1);
|
nfs_free_user_pages(dreq->pages, dreq->npages, 1);
|
||||||
|
|
||||||
if (dreq->iocb) {
|
if (dreq->iocb) {
|
||||||
long res = atomic_read(&dreq->error);
|
long res = (long) dreq->error;
|
||||||
if (!res)
|
if (!res)
|
||||||
res = atomic_read(&dreq->count);
|
res = (long) dreq->count;
|
||||||
aio_complete(dreq->iocb, res, 0);
|
aio_complete(dreq->iocb, res, 0);
|
||||||
} else
|
} else
|
||||||
wake_up(&dreq->wait);
|
wake_up(&dreq->wait);
|
||||||
@ -233,7 +231,6 @@ static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, size_t rsize)
|
|||||||
{
|
{
|
||||||
struct list_head *list;
|
struct list_head *list;
|
||||||
struct nfs_direct_req *dreq;
|
struct nfs_direct_req *dreq;
|
||||||
unsigned int reads = 0;
|
|
||||||
unsigned int rpages = (rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
unsigned int rpages = (rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||||
|
|
||||||
dreq = nfs_direct_req_alloc();
|
dreq = nfs_direct_req_alloc();
|
||||||
@ -259,13 +256,12 @@ static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, size_t rsize)
|
|||||||
list_add(&data->pages, list);
|
list_add(&data->pages, list);
|
||||||
|
|
||||||
data->req = (struct nfs_page *) dreq;
|
data->req = (struct nfs_page *) dreq;
|
||||||
reads++;
|
dreq->outstanding++;
|
||||||
if (nbytes <= rsize)
|
if (nbytes <= rsize)
|
||||||
break;
|
break;
|
||||||
nbytes -= rsize;
|
nbytes -= rsize;
|
||||||
}
|
}
|
||||||
kref_get(&dreq->kref);
|
kref_get(&dreq->kref);
|
||||||
atomic_set(&dreq->complete, reads);
|
|
||||||
return dreq;
|
return dreq;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,13 +272,21 @@ static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
|
|||||||
|
|
||||||
if (nfs_readpage_result(task, data) != 0)
|
if (nfs_readpage_result(task, data) != 0)
|
||||||
return;
|
return;
|
||||||
if (likely(task->tk_status >= 0))
|
|
||||||
atomic_add(data->res.count, &dreq->count);
|
|
||||||
else
|
|
||||||
atomic_set(&dreq->error, task->tk_status);
|
|
||||||
|
|
||||||
if (unlikely(atomic_dec_and_test(&dreq->complete)))
|
spin_lock(&dreq->lock);
|
||||||
nfs_direct_complete(dreq);
|
|
||||||
|
if (likely(task->tk_status >= 0))
|
||||||
|
dreq->count += data->res.count;
|
||||||
|
else
|
||||||
|
dreq->error = task->tk_status;
|
||||||
|
|
||||||
|
if (--dreq->outstanding) {
|
||||||
|
spin_unlock(&dreq->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&dreq->lock);
|
||||||
|
nfs_direct_complete(dreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct rpc_call_ops nfs_read_direct_ops = {
|
static const struct rpc_call_ops nfs_read_direct_ops = {
|
||||||
@ -388,7 +392,6 @@ static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize
|
|||||||
{
|
{
|
||||||
struct list_head *list;
|
struct list_head *list;
|
||||||
struct nfs_direct_req *dreq;
|
struct nfs_direct_req *dreq;
|
||||||
unsigned int writes = 0;
|
|
||||||
unsigned int wpages = (wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
unsigned int wpages = (wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||||
|
|
||||||
dreq = nfs_direct_req_alloc();
|
dreq = nfs_direct_req_alloc();
|
||||||
@ -414,16 +417,19 @@ static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize
|
|||||||
list_add(&data->pages, list);
|
list_add(&data->pages, list);
|
||||||
|
|
||||||
data->req = (struct nfs_page *) dreq;
|
data->req = (struct nfs_page *) dreq;
|
||||||
writes++;
|
dreq->outstanding++;
|
||||||
if (nbytes <= wsize)
|
if (nbytes <= wsize)
|
||||||
break;
|
break;
|
||||||
nbytes -= wsize;
|
nbytes -= wsize;
|
||||||
}
|
}
|
||||||
kref_get(&dreq->kref);
|
kref_get(&dreq->kref);
|
||||||
atomic_set(&dreq->complete, writes);
|
|
||||||
return dreq;
|
return dreq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NB: Return the value of the first error return code. Subsequent
|
||||||
|
* errors after the first one are ignored.
|
||||||
|
*/
|
||||||
static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
|
static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
|
||||||
{
|
{
|
||||||
struct nfs_write_data *data = calldata;
|
struct nfs_write_data *data = calldata;
|
||||||
@ -436,15 +442,22 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
|
|||||||
if (unlikely(data->res.verf->committed != NFS_FILE_SYNC))
|
if (unlikely(data->res.verf->committed != NFS_FILE_SYNC))
|
||||||
status = -EIO;
|
status = -EIO;
|
||||||
|
|
||||||
if (likely(status >= 0))
|
spin_lock(&dreq->lock);
|
||||||
atomic_add(data->res.count, &dreq->count);
|
|
||||||
else
|
|
||||||
atomic_set(&dreq->error, status);
|
|
||||||
|
|
||||||
if (unlikely(atomic_dec_and_test(&dreq->complete))) {
|
if (likely(status >= 0))
|
||||||
nfs_end_data_update(data->inode);
|
dreq->count += data->res.count;
|
||||||
nfs_direct_complete(dreq);
|
else
|
||||||
|
dreq->error = status;
|
||||||
|
|
||||||
|
if (--dreq->outstanding) {
|
||||||
|
spin_unlock(&dreq->lock);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock(&dreq->lock);
|
||||||
|
|
||||||
|
nfs_end_data_update(data->inode);
|
||||||
|
nfs_direct_complete(dreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct rpc_call_ops nfs_write_direct_ops = {
|
static const struct rpc_call_ops nfs_write_direct_ops = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user