summaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/client.c3
-rw-r--r--fs/nfs/dir.c25
-rw-r--r--fs/nfs/direct.c5
-rw-r--r--fs/nfs/file.c7
-rw-r--r--fs/nfs/inode.c3
-rw-r--r--fs/nfs/internal.h12
-rw-r--r--fs/nfs/mount_clnt.c7
-rw-r--r--fs/nfs/nfs2xdr.c7
-rw-r--r--fs/nfs/nfs3xdr.c13
-rw-r--r--fs/nfs/nfs4proc.c4
-rw-r--r--fs/nfs/nfs4xdr.c7
-rw-r--r--fs/nfs/nfsroot.c2
-rw-r--r--fs/nfs/pagelist.c242
-rw-r--r--fs/nfs/read.c92
-rw-r--r--fs/nfs/super.c10
-rw-r--r--fs/nfs/symlink.c6
-rw-r--r--fs/nfs/write.c258
17 files changed, 310 insertions, 393 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 2190e6c..5bd03b9 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -618,7 +618,8 @@ static int nfs_init_server(struct nfs_server *server, const struct nfs_mount_dat
if (clp->cl_nfsversion == 3) {
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN;
- server->caps |= NFS_CAP_READDIRPLUS;
+ if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
+ server->caps |= NFS_CAP_READDIRPLUS;
} else {
if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
server->namelen = NFS2_MAXNAMLEN;
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index cd34697..625d8e5 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -154,6 +154,8 @@ typedef struct {
decode_dirent_t decode;
int plus;
int error;
+ unsigned long timestamp;
+ int timestamp_valid;
} nfs_readdir_descriptor_t;
/* Now we cache directories properly, by stuffing the dirent
@@ -195,6 +197,8 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
}
goto error;
}
+ desc->timestamp = timestamp;
+ desc->timestamp_valid = 1;
SetPageUptodate(page);
spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME;
@@ -225,6 +229,10 @@ int dir_decode(nfs_readdir_descriptor_t *desc)
if (IS_ERR(p))
return PTR_ERR(p);
desc->ptr = p;
+ if (desc->timestamp_valid)
+ desc->entry->fattr->time_start = desc->timestamp;
+ else
+ desc->entry->fattr->valid &= ~NFS_ATTR_FATTR;
return 0;
}
@@ -316,14 +324,16 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
__FUNCTION__, desc->page_index,
(long long) *desc->dir_cookie);
+ /* If we find the page in the page_cache, we cannot be sure
+ * how fresh the data is, so we will ignore readdir_plus attributes.
+ */
+ desc->timestamp_valid = 0;
page = read_cache_page(inode->i_mapping, desc->page_index,
(filler_t *)nfs_readdir_filler, desc);
if (IS_ERR(page)) {
status = PTR_ERR(page);
goto out;
}
- if (!PageUptodate(page))
- goto read_error;
/* NOTE: Someone else may have changed the READDIRPLUS flag */
desc->page = page;
@@ -337,9 +347,6 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
out:
dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __FUNCTION__, status);
return status;
- read_error:
- page_cache_release(page);
- return -EIO;
}
/*
@@ -468,6 +475,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
struct rpc_cred *cred = nfs_file_cred(file);
struct page *page = NULL;
int status;
+ unsigned long timestamp;
dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
(unsigned long long)*desc->dir_cookie);
@@ -477,6 +485,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
status = -ENOMEM;
goto out;
}
+ timestamp = jiffies;
desc->error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, *desc->dir_cookie,
page,
NFS_SERVER(inode)->dtsize,
@@ -487,6 +496,8 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
desc->page = page;
desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */
if (desc->error >= 0) {
+ desc->timestamp = timestamp;
+ desc->timestamp_valid = 1;
if ((status = dir_decode(desc)) == 0)
desc->entry->prev_cookie = *desc->dir_cookie;
} else
@@ -849,6 +860,10 @@ static int nfs_dentry_delete(struct dentry *dentry)
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{
nfs_inode_return_delegation(inode);
+ if (S_ISDIR(inode->i_mode))
+ /* drop any readdir cache as it could easily be old */
+ NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
+
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
lock_kernel();
drop_nlink(inode);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 2877744c..889de60 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -54,6 +54,7 @@
#include <asm/uaccess.h>
#include <asm/atomic.h>
+#include "internal.h"
#include "iostat.h"
#define NFSDBG_FACILITY NFSDBG_VFS
@@ -271,7 +272,7 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
bytes = min(rsize,count);
result = -ENOMEM;
- data = nfs_readdata_alloc(pgbase + bytes);
+ data = nfs_readdata_alloc(nfs_page_array_len(pgbase, bytes));
if (unlikely(!data))
break;
@@ -602,7 +603,7 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
bytes = min(wsize,count);
result = -ENOMEM;
- data = nfs_writedata_alloc(pgbase + bytes);
+ data = nfs_writedata_alloc(nfs_page_array_len(pgbase, bytes));
if (unlikely(!data))
break;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 8e66b5a..5eaee6d 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -391,17 +391,12 @@ out_swapfile:
static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
{
- struct file_lock cfl;
struct inode *inode = filp->f_mapping->host;
int status = 0;
lock_kernel();
/* Try local locking first */
- if (posix_test_lock(filp, fl, &cfl)) {
- fl->fl_start = cfl.fl_start;
- fl->fl_end = cfl.fl_end;
- fl->fl_type = cfl.fl_type;
- fl->fl_pid = cfl.fl_pid;
+ if (posix_test_lock(filp, fl)) {
goto out;
}
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 44aa9b7..1e9a915 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1167,8 +1167,7 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag
{
struct nfs_inode *nfsi = (struct nfs_inode *) foo;
- if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
- SLAB_CTOR_CONSTRUCTOR) {
+ if (flags & SLAB_CTOR_CONSTRUCTOR) {
inode_init_once(&nfsi->vfs_inode);
spin_lock_init(&nfsi->req_lock);
INIT_LIST_HEAD(&nfsi->dirty);
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 6610f2b..ad2b40d 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -231,3 +231,15 @@ unsigned int nfs_page_length(struct page *page)
}
return 0;
}
+
+/*
+ * Determine the number of pages in an array of length 'len' and
+ * with a base offset of 'base'
+ */
+static inline
+unsigned int nfs_page_array_len(unsigned int base, size_t len)
+{
+ return ((unsigned long)len + (unsigned long)base +
+ PAGE_SIZE - 1) >> PAGE_SHIFT;
+}
+
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index f75fe72..ca5a266 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -133,13 +133,15 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res)
#define MNT_dirpath_sz (1 + 256)
#define MNT_fhstatus_sz (1 + 8)
+#define MNT_fhstatus3_sz (1 + 16)
static struct rpc_procinfo mnt_procedures[] = {
[MNTPROC_MNT] = {
.p_proc = MNTPROC_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus,
- .p_bufsiz = MNT_dirpath_sz << 2,
+ .p_arglen = MNT_dirpath_sz,
+ .p_replen = MNT_fhstatus_sz,
.p_statidx = MNTPROC_MNT,
.p_name = "MOUNT",
},
@@ -150,7 +152,8 @@ static struct rpc_procinfo mnt3_procedures[] = {
.p_proc = MOUNTPROC3_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus3,
- .p_bufsiz = MNT_dirpath_sz << 2,
+ .p_arglen = MNT_dirpath_sz,
+ .p_replen = MNT_fhstatus3_sz,
.p_statidx = MOUNTPROC3_MNT,
.p_name = "MOUNT",
},
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 3be4e72..abd9f8b 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -687,16 +687,13 @@ nfs_stat_to_errno(int stat)
return nfs_errtbl[i].errno;
}
-#ifndef MAX
-# define MAX(a, b) (((a) > (b))? (a) : (b))
-#endif
-
#define PROC(proc, argtype, restype, timer) \
[NFSPROC_##proc] = { \
.p_proc = NFSPROC_##proc, \
.p_encode = (kxdrproc_t) nfs_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs_xdr_##restype, \
- .p_bufsiz = MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \
+ .p_arglen = NFS_##argtype##_sz, \
+ .p_replen = NFS_##restype##_sz, \
.p_timer = timer, \
.p_statidx = NFSPROC_##proc, \
.p_name = #proc, \
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 0ace092..b51df8eb 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -1102,16 +1102,13 @@ nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
}
#endif /* CONFIG_NFS_V3_ACL */
-#ifndef MAX
-# define MAX(a, b) (((a) > (b))? (a) : (b))
-#endif
-
#define PROC(proc, argtype, restype, timer) \
[NFS3PROC_##proc] = { \
.p_proc = NFS3PROC_##proc, \
.p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
- .p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \
+ .p_arglen = NFS3_##argtype##_sz, \
+ .p_replen = NFS3_##restype##_sz, \
.p_timer = timer, \
.p_statidx = NFS3PROC_##proc, \
.p_name = #proc, \
@@ -1153,7 +1150,8 @@ static struct rpc_procinfo nfs3_acl_procedures[] = {
.p_proc = ACLPROC3_GETACL,
.p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
.p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
- .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2,
+ .p_arglen = ACL3_getaclargs_sz,
+ .p_replen = ACL3_getaclres_sz,
.p_timer = 1,
.p_name = "GETACL",
},
@@ -1161,7 +1159,8 @@ static struct rpc_procinfo nfs3_acl_procedures[] = {
.p_proc = ACLPROC3_SETACL,
.p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
.p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
- .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2,
+ .p_arglen = ACL3_setaclargs_sz,
+ .p_replen = ACL3_setaclres_sz,
.p_timer = 0,
.p_name = "SETACL",
},
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index f52cf5c..d6a30e9 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2647,8 +2647,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
nfs_inode_return_delegation(inode);
buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
- if (ret == 0)
- nfs4_write_cached_acl(inode, buf, buflen);
+ nfs_zap_caches(inode);
return ret;
}
@@ -3018,6 +3017,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
case -NFS4ERR_DENIED:
status = 0;
}
+ request->fl_ops->fl_release_private(request);
out:
up_read(&clp->cl_sem);
return status;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index f02d522..b8c28f2 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4546,16 +4546,13 @@ nfs4_stat_to_errno(int stat)
return stat;
}
-#ifndef MAX
-# define MAX(a, b) (((a) > (b))? (a) : (b))
-#endif
-
#define PROC(proc, argtype, restype) \
[NFSPROC4_CLNT_##proc] = { \
.p_proc = NFSPROC4_COMPOUND, \
.p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs4_xdr_##restype, \
- .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \
+ .p_arglen = NFS4_##argtype##_sz, \
+ .p_replen = NFS4_##restype##_sz, \
.p_statidx = NFSPROC4_CLNT_##proc, \
.p_name = #proc, \
}
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index 75f819d..49d1008 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -428,7 +428,7 @@ static int __init root_nfs_getport(int program, int version, int proto)
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n",
program, version, NIPQUAD(servaddr));
set_sockaddr(&sin, servaddr, 0);
- return rpc_getport_external(&sin, program, version, proto);
+ return rpcb_getport_external(&sin, program, version, proto);
}
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index ca4b1d4..3889501 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -17,7 +17,8 @@
#include <linux/nfs_page.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
-#include <linux/writeback.h>
+
+#include "internal.h"
#define NFS_PARANOIA 1
@@ -50,9 +51,7 @@ nfs_page_free(struct nfs_page *p)
* @count: number of bytes to read/write
*
* The page must be locked by the caller. This makes sure we never
- * create two different requests for the same page, and avoids
- * a possible deadlock when we reach the hard limit on the number
- * of dirty pages.
+ * create two different requests for the same page.
* User should ensure it is safe to sleep in this function.
*/
struct nfs_page *
@@ -63,16 +62,12 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_page *req;
- /* Deal with hard limits. */
for (;;) {
/* try to allocate the request struct */
req = nfs_page_alloc();
if (req != NULL)
break;
- /* Try to free up at least one request in order to stay
- * below the hard limit
- */
if (signalled() && (server->flags & NFS_MOUNT_INTR))
return ERR_PTR(-ERESTARTSYS);
yield();
@@ -223,124 +218,151 @@ out:
}
/**
- * nfs_coalesce_requests - Split coalesced requests out from a list.
- * @head: source list
- * @dst: destination list
- * @nmax: maximum number of requests to coalesce
- *
- * Moves a maximum of 'nmax' elements from one list to another.
- * The elements are checked to ensure that they form a contiguous set
- * of pages, and that the RPC credentials are the same.
+ * nfs_pageio_init - initialise a page io descriptor
+ * @desc: pointer to descriptor
+ * @inode: pointer to inode
+ * @doio: pointer to io function
+ * @bsize: io block size
+ * @io_flags: extra parameters for the io function
*/
-int
-nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
- unsigned int nmax)
+void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
+ struct inode *inode,
+ int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int),
+ size_t bsize,
+ int io_flags)
{
- struct nfs_page *req = NULL;
- unsigned int npages = 0;
-
- while (!list_empty(head)) {
- struct nfs_page *prev = req;
-
- req = nfs_list_entry(head->next);
- if (prev) {
- if (req->wb_context->cred != prev->wb_context->cred)
- break;
- if (req->wb_context->lockowner != prev->wb_context->lockowner)
- break;
- if (req->wb_context->state != prev->wb_context->state)
- break;
- if (req->wb_index != (prev->wb_index + 1))
- break;
-
- if (req->wb_pgbase != 0)
- break;
- }
- nfs_list_remove_request(req);
- nfs_list_add_request(req, dst);
- npages++;
- if (req->wb_pgbase + req->wb_bytes != PAGE_CACHE_SIZE)
- break;
- if (npages >= nmax)
- break;
- }
- return npages;
+ INIT_LIST_HEAD(&desc->pg_list);
+ desc->pg_bytes_written = 0;
+ desc->pg_count = 0;
+ desc->pg_bsize = bsize;
+ desc->pg_base = 0;
+ desc->pg_inode = inode;
+ desc->pg_doio = doio;
+ desc->pg_ioflags = io_flags;
+ desc->pg_error = 0;
}
-#define NFS_SCAN_MAXENTRIES 16
/**
- * nfs_scan_dirty - Scan the radix tree for dirty requests
- * @mapping: pointer to address space
- * @wbc: writeback_control structure
- * @dst: Destination list
+ * nfs_can_coalesce_requests - test two requests for compatibility
+ * @prev: pointer to nfs_page
+ * @req: pointer to nfs_page
*
- * Moves elements from one of the inode request lists.
- * If the number of requests is set to 0, the entire address_space
- * starting at index idx_start, is scanned.
- * The requests are *not* checked to ensure that they form a contiguous set.
- * You must be holding the inode's req_lock when calling this function
+ * The nfs_page structures 'prev' and 'req' are compared to ensure that the
+ * page data area they describe is contiguous, and that their RPC
+ * credentials, NFSv4 open state, and lockowners are the same.
+ *
+ * Return 'true' if this is the case, else return 'false'.
*/
-long nfs_scan_dirty(struct address_space *mapping,
- struct writeback_control *wbc,
- struct list_head *dst)
+static int nfs_can_coalesce_requests(struct nfs_page *prev,
+ struct nfs_page *req)
{
- struct nfs_inode *nfsi = NFS_I(mapping->host);
- struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES];
- struct nfs_page *req;
- pgoff_t idx_start, idx_end;
- long res = 0;
- int found, i;
-
- if (nfsi->ndirty == 0)
+ if (req->wb_context->cred != prev->wb_context->cred)
return 0;
- if (wbc->range_cyclic) {
- idx_start = 0;
- idx_end = ULONG_MAX;
- } else if (wbc->range_end == 0) {
- idx_start = wbc->range_start >> PAGE_CACHE_SHIFT;
- idx_end = ULONG_MAX;
- } else {
- idx_start = wbc->range_start >> PAGE_CACHE_SHIFT;
- idx_end = wbc->range_end >> PAGE_CACHE_SHIFT;
- }
+ if (req->wb_context->lockowner != prev->wb_context->lockowner)
+ return 0;
+ if (req->wb_context->state != prev->wb_context->state)
+ return 0;
+ if (req->wb_index != (prev->wb_index + 1))
+ return 0;
+ if (req->wb_pgbase != 0)
+ return 0;
+ if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
+ return 0;
+ return 1;
+}
- for (;;) {
- unsigned int toscan = NFS_SCAN_MAXENTRIES;
+/**
+ * nfs_pageio_do_add_request - Attempt to coalesce a request into a page list.
+ * @desc: destination io descriptor
+ * @req: request
+ *
+ * Returns true if the request 'req' was successfully coalesced into the
+ * existing list of pages 'desc'.
+ */
+static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
+ struct nfs_page *req)
+{
+ size_t newlen = req->wb_bytes;
- found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree,
- (void **)&pgvec[0], idx_start, toscan,
- NFS_PAGE_TAG_DIRTY);
+ if (desc->pg_count != 0) {
+ struct nfs_page *prev;
- /* Did we make progress? */
- if (found <= 0)
- break;
+ /*
+ * FIXME: ideally we should be able to coalesce all requests
+ * that are not block boundary aligned, but currently this
+ * is problematic for the case of bsize < PAGE_CACHE_SIZE,
+ * since nfs_flush_multi and nfs_pagein_multi assume you
+ * can have only one struct nfs_page.
+ */
+ if (desc->pg_bsize < PAGE_SIZE)
+ return 0;
+ newlen += desc->pg_count;
+ if (newlen > desc->pg_bsize)
+ return 0;
+ prev = nfs_list_entry(desc->pg_list.prev);
+ if (!nfs_can_coalesce_requests(prev, req))
+ return 0;
+ } else
+ desc->pg_base = req->wb_pgbase;
+ nfs_list_remove_request(req);
+ nfs_list_add_request(req, &desc->pg_list);
+ desc->pg_count = newlen;
+ return 1;
+}
- for (i = 0; i < found; i++) {
- req = pgvec[i];
- if (!wbc->range_cyclic && req->wb_index > idx_end)
- goto out;
+/*
+ * Helper for nfs_pageio_add_request and nfs_pageio_complete
+ */
+static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
+{
+ if (!list_empty(&desc->pg_list)) {
+ int error = desc->pg_doio(desc->pg_inode,
+ &desc->pg_list,
+ nfs_page_array_len(desc->pg_base,
+ desc->pg_count),
+ desc->pg_count,
+ desc->pg_ioflags);
+ if (error < 0)
+ desc->pg_error = error;
+ else
+ desc->pg_bytes_written += desc->pg_count;
+ }
+ if (list_empty(&desc->pg_list)) {
+ desc->pg_count = 0;
+ desc->pg_base = 0;
+ }
+}
- /* Try to lock request and mark it for writeback */
- if (!nfs_set_page_writeback_locked(req))
- goto next;
- radix_tree_tag_clear(&nfsi->nfs_page_tree,
- req->wb_index, NFS_PAGE_TAG_DIRTY);
- nfsi->ndirty--;
- nfs_list_remove_request(req);
- nfs_list_add_request(req, dst);
- res++;
- if (res == LONG_MAX)
- goto out;
-next:
- idx_start = req->wb_index + 1;
- }
+/**
+ * nfs_pageio_add_request - Attempt to coalesce a request into a page list.
+ * @desc: destination io descriptor
+ * @req: request
+ *
+ * Returns true if the request 'req' was successfully coalesced into the
+ * existing list of pages 'desc'.
+ */
+int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
+ struct nfs_page *req)
+{
+ while (!nfs_pageio_do_add_request(desc, req)) {
+ nfs_pageio_doio(desc);
+ if (desc->pg_error < 0)
+ return 0;
}
-out:
- WARN_ON ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty));
- return res;
+ return 1;
}
/**
+ * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
+ * @desc: pointer to io descriptor
+ */
+void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
+{
+ nfs_pageio_doio(desc);
+}
+
+#define NFS_SCAN_MAXENTRIES 16
+/**
* nfs_scan_list - Scan a list for matching requests
* @nfsi: NFS inode
* @head: One of the NFS inode request lists
@@ -355,12 +377,12 @@ out:
* You must be holding the inode's req_lock when calling this function
*/
int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head,
- struct list_head *dst, unsigned long idx_start,
+ struct list_head *dst, pgoff_t idx_start,
unsigned int npages)
{
struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES];
struct nfs_page *req;
- unsigned long idx_end;
+ pgoff_t idx_end;
int found, i;
int res;
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 6ab4d5a..9a55807 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -27,7 +27,8 @@
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
-static int nfs_pagein_one(struct list_head *, struct inode *);
+static int nfs_pagein_multi(struct inode *, struct list_head *, unsigned int, size_t, int);
+static int nfs_pagein_one(struct inode *, struct list_head *, unsigned int, size_t, int);
static const struct rpc_call_ops nfs_read_partial_ops;
static const struct rpc_call_ops nfs_read_full_ops;
@@ -36,9 +37,8 @@ static mempool_t *nfs_rdata_mempool;
#define MIN_POOL_READ (32)
-struct nfs_read_data *nfs_readdata_alloc(size_t len)
+struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
{
- unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS);
if (p) {
@@ -133,7 +133,10 @@ static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len);
nfs_list_add_request(new, &one_request);
- nfs_pagein_one(&one_request, inode);
+ if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
+ nfs_pagein_multi(inode, &one_request, 1, len, 0);
+ else
+ nfs_pagein_one(inode, &one_request, 1, len, 0);
return 0;
}
@@ -230,7 +233,7 @@ static void nfs_execute_read(struct nfs_read_data *data)
* won't see the new data until our attribute cache is updated. This is more
* or less conventional NFS client behavior.
*/
-static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
+static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
{
struct nfs_page *req = nfs_list_entry(head->next);
struct page *page = req->wb_page;
@@ -242,11 +245,11 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
nfs_list_remove_request(req);
- nbytes = req->wb_bytes;
+ nbytes = count;
do {
size_t len = min(nbytes,rsize);
- data = nfs_readdata_alloc(len);
+ data = nfs_readdata_alloc(1);
if (!data)
goto out_bad;
INIT_LIST_HEAD(&data->pages);
@@ -258,23 +261,19 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
ClearPageError(page);
offset = 0;
- nbytes = req->wb_bytes;
+ nbytes = count;
do {
data = list_entry(list.next, struct nfs_read_data, pages);
list_del_init(&data->pages);
data->pagevec[0] = page;
- if (nbytes > rsize) {
- nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
- rsize, offset);
- offset += rsize;
- nbytes -= rsize;
- } else {
- nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
- nbytes, offset);
- nbytes = 0;
- }
+ if (nbytes < rsize)
+ rsize = nbytes;
+ nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
+ rsize, offset);
+ offset += rsize;
+ nbytes -= rsize;
nfs_execute_read(data);
} while (nbytes != 0);
@@ -291,30 +290,24 @@ out_bad:
return -ENOMEM;
}
-static int nfs_pagein_one(struct list_head *head, struct inode *inode)
+static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
{
struct nfs_page *req;
struct page **pages;
struct nfs_read_data *data;
- unsigned int count;
- if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
- return nfs_pagein_multi(head, inode);
-
- data = nfs_readdata_alloc(NFS_SERVER(inode)->rsize);
+ data = nfs_readdata_alloc(npages);
if (!data)
goto out_bad;
INIT_LIST_HEAD(&data->pages);
pages = data->pagevec;
- count = 0;
while (!list_empty(head)) {
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_list_add_request(req, &data->pages);
ClearPageError(req->wb_page);
*pages++ = req->wb_page;
- count += req->wb_bytes;
}
req = nfs_list_entry(data->pages.next);
@@ -327,28 +320,6 @@ out_bad:
return -ENOMEM;
}
-static int
-nfs_pagein_list(struct list_head *head, int rpages)
-{
- LIST_HEAD(one_request);
- struct nfs_page *req;
- int error = 0;
- unsigned int pages = 0;
-
- while (!list_empty(head)) {
- pages += nfs_coalesce_requests(head, &one_request, rpages);
- req = nfs_list_entry(one_request.next);
- error = nfs_pagein_one(&one_request, req->wb_context->dentry->d_inode);
- if (error < 0)
- break;
- }
- if (error >= 0)
- return pages;
-
- nfs_async_read_error(head);
- return error;
-}
-
/*
* This is the callback from RPC telling us whether a reply was
* received or some error occurred (timeout or socket shutdown).
@@ -538,7 +509,7 @@ out_error:
}
struct nfs_readdesc {
- struct list_head *head;
+ struct nfs_pageio_descriptor *pgio;
struct nfs_open_context *ctx;
};
@@ -562,19 +533,21 @@ readpage_async_filler(void *data, struct page *page)
}
if (len < PAGE_CACHE_SIZE)
memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len);
- nfs_list_add_request(new, desc->head);
+ nfs_pageio_add_request(desc->pgio, new);
return 0;
}
int nfs_readpages(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages)
{
- LIST_HEAD(head);
+ struct nfs_pageio_descriptor pgio;
struct nfs_readdesc desc = {
- .head = &head,
+ .pgio = &pgio,
};
struct inode *inode = mapping->host;
struct nfs_server *server = NFS_SERVER(inode);
+ size_t rsize = server->rsize;
+ unsigned long npages;
int ret = -ESTALE;
dprintk("NFS: nfs_readpages (%s/%Ld %d)\n",
@@ -593,13 +566,16 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
} else
desc.ctx = get_nfs_open_context((struct nfs_open_context *)
filp->private_data);
+ if (rsize < PAGE_CACHE_SIZE)
+ nfs_pageio_init(&pgio, inode, nfs_pagein_multi, rsize, 0);
+ else
+ nfs_pageio_init(&pgio, inode, nfs_pagein_one, rsize, 0);
+
ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
- if (!list_empty(&head)) {
- int err = nfs_pagein_list(&head, server->rpages);
- if (!ret)
- nfs_add_stats(inode, NFSIOS_READPAGES, err);
- ret = err;
- }
+
+ nfs_pageio_complete(&pgio);
+ npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ nfs_add_stats(inode, NFSIOS_READPAGES, npages);
put_nfs_open_context(desc.ctx);
out:
return ret;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index f1eae44..ca20d3c 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -204,9 +204,9 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
lock_kernel();
error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
- buf->f_type = NFS_SUPER_MAGIC;
if (error < 0)
goto out_err;
+ buf->f_type = NFS_SUPER_MAGIC;
/*
* Current versions of glibc do not correctly handle the
@@ -233,15 +233,14 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_ffree = res.afiles;
buf->f_namelen = server->namelen;
- out:
+
unlock_kernel();
return 0;
out_err:
dprintk("%s: statfs error = %d\n", __FUNCTION__, -error);
- buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
- goto out;
-
+ unlock_kernel();
+ return error;
}
/*
@@ -291,6 +290,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
{ NFS_MOUNT_NOAC, ",noac", "" },
{ NFS_MOUNT_NONLM, ",nolock", "" },
{ NFS_MOUNT_NOACL, ",noacl", "" },
+ { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
{ 0, NULL, NULL }
};
const struct proc_nfs_info *nfs_infop;
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index f4a0548..bc28213 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -61,15 +61,9 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd)
err = page;
goto read_failed;
}
- if (!PageUptodate(page)) {
- err = ERR_PTR(-EIO);
- goto getlink_read_error;
- }
nd_set_link(nd, kmap(page));
return page;
-getlink_read_error:
- page_cache_release(page);
read_failed:
nd_set_link(nd, err);
return NULL;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 7975589..5d44b8b 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -38,7 +38,8 @@
static struct nfs_page * nfs_update_request(struct nfs_open_context*,
struct page *,
unsigned int, unsigned int);
-static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how);
+static void nfs_pageio_init_write(struct nfs_pageio_descriptor *desc,
+ struct inode *inode, int ioflags);
static const struct rpc_call_ops nfs_write_partial_ops;
static const struct rpc_call_ops nfs_write_full_ops;
static const struct rpc_call_ops nfs_commit_ops;
@@ -71,9 +72,8 @@ void nfs_commit_free(struct nfs_write_data *wdata)
call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free);
}
-struct nfs_write_data *nfs_writedata_alloc(size_t len)
+struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
{
- unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
if (p) {
@@ -139,7 +139,7 @@ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int c
{
struct inode *inode = page->mapping->host;
loff_t end, i_size = i_size_read(inode);
- unsigned long end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
+ pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
if (i_size > 0 && page->index < end_index)
return;
@@ -201,7 +201,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
static int wb_priority(struct writeback_control *wbc)
{
if (wbc->for_reclaim)
- return FLUSH_HIGHPRI;
+ return FLUSH_HIGHPRI | FLUSH_STABLE;
if (wbc->for_kupdate)
return FLUSH_LOWPRI;
return 0;
@@ -251,7 +251,8 @@ static void nfs_end_page_writeback(struct page *page)
* was not tagged.
* May also return an error if the user signalled nfs_wait_on_request().
*/
-static int nfs_page_mark_flush(struct page *page)
+static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
+ struct page *page)
{
struct nfs_page *req;
struct nfs_inode *nfsi = NFS_I(page->mapping->host);
@@ -273,6 +274,8 @@ static int nfs_page_mark_flush(struct page *page)
* request as dirty (in which case we don't care).
*/
spin_unlock(req_lock);
+ /* Prevent deadlock! */
+ nfs_pageio_complete(pgio);
ret = nfs_wait_on_request(req);
nfs_release_request(req);
if (ret != 0)
@@ -283,21 +286,18 @@ static int nfs_page_mark_flush(struct page *page)
/* This request is marked for commit */
spin_unlock(req_lock);
nfs_unlock_request(req);
+ nfs_pageio_complete(pgio);
return 1;
}
- if (nfs_set_page_writeback(page) == 0) {
- nfs_list_remove_request(req);
- /* add the request to the inode's dirty list. */
- radix_tree_tag_set(&nfsi->nfs_page_tree,
- req->wb_index, NFS_PAGE_TAG_DIRTY);
- nfs_list_add_request(req, &nfsi->dirty);
- nfsi->ndirty++;
- spin_unlock(req_lock);
- __mark_inode_dirty(page->mapping->host, I_DIRTY_PAGES);
- } else
+ if (nfs_set_page_writeback(page) != 0) {
spin_unlock(req_lock);
+ BUG();
+ }
+ radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
+ NFS_PAGE_TAG_WRITEBACK);
ret = test_bit(PG_NEED_FLUSH, &req->wb_flags);
- nfs_unlock_request(req);
+ spin_unlock(req_lock);
+ nfs_pageio_add_request(pgio, req);
return ret;
}
@@ -306,6 +306,7 @@ static int nfs_page_mark_flush(struct page *page)
*/
static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc)
{
+ struct nfs_pageio_descriptor mypgio, *pgio;
struct nfs_open_context *ctx;
struct inode *inode = page->mapping->host;
unsigned offset;
@@ -314,7 +315,14 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1);
- err = nfs_page_mark_flush(page);
+ if (wbc->for_writepages)
+ pgio = wbc->fs_private;
+ else {
+ nfs_pageio_init_write(&mypgio, inode, wb_priority(wbc));
+ pgio = &mypgio;
+ }
+
+ err = nfs_page_async_flush(pgio, page);
if (err <= 0)
goto out;
err = 0;
@@ -331,12 +339,12 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
put_nfs_open_context(ctx);
if (err != 0)
goto out;
- err = nfs_page_mark_flush(page);
+ err = nfs_page_async_flush(pgio, page);
if (err > 0)
err = 0;
out:
if (!wbc->for_writepages)
- nfs_flush_mapping(page->mapping, wbc, FLUSH_STABLE|wb_priority(wbc));
+ nfs_pageio_complete(pgio);
return err;
}
@@ -352,20 +360,20 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
{
struct inode *inode = mapping->host;
+ struct nfs_pageio_descriptor pgio;
int err;
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
+ nfs_pageio_init_write(&pgio, inode, wb_priority(wbc));
+ wbc->fs_private = &pgio;
err = generic_writepages(mapping, wbc);
+ nfs_pageio_complete(&pgio);
if (err)
return err;
- err = nfs_flush_mapping(mapping, wbc, wb_priority(wbc));
- if (err < 0)
- goto out;
- nfs_add_stats(inode, NFSIOS_WRITEPAGES, err);
- err = 0;
-out:
- return err;
+ if (pgio.pg_error)
+ return pgio.pg_error;
+ return 0;
}
/*
@@ -503,11 +511,11 @@ int nfs_reschedule_unstable_write(struct nfs_page *req)
*
* Interruptible by signals only if mounted with intr flag.
*/
-static int nfs_wait_on_requests_locked(struct inode *inode, unsigned long idx_start, unsigned int npages)
+static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, unsigned int npages)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req;
- unsigned long idx_end, next;
+ pgoff_t idx_end, next;
unsigned int res = 0;
int error;
@@ -536,18 +544,6 @@ static int nfs_wait_on_requests_locked(struct inode *inode, unsigned long idx_st
return res;
}
-static void nfs_cancel_dirty_list(struct list_head *head)
-{
- struct nfs_page *req;
- while(!list_empty(head)) {
- req = nfs_list_entry(head->next);
- nfs_list_remove_request(req);
- nfs_end_page_writeback(req->wb_page);
- nfs_inode_remove_request(req);
- nfs_clear_page_writeback(req);
- }
-}
-
static void nfs_cancel_commit_list(struct list_head *head)
{
struct nfs_page *req;
@@ -574,7 +570,7 @@ static void nfs_cancel_commit_list(struct list_head *head)
* The requests are *not* checked to ensure that they form a contiguous set.
*/
static int
-nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
{
struct nfs_inode *nfsi = NFS_I(inode);
int res = 0;
@@ -588,40 +584,12 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_st
return res;
}
#else
-static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
{
return 0;
}
#endif
-static int nfs_wait_on_write_congestion(struct address_space *mapping)
-{
- struct inode *inode = mapping->host;
- struct backing_dev_info *bdi = mapping->backing_dev_info;
- int ret = 0;
-
- might_sleep();
-
- if (!bdi_write_congested(bdi))
- return 0;
-
- nfs_inc_stats(inode, NFSIOS_CONGESTIONWAIT);
-
- do {
- struct rpc_clnt *clnt = NFS_CLIENT(inode);
- sigset_t oldset;
-
- rpc_clnt_sigmask(clnt, &oldset);
- ret = congestion_wait_interruptible(WRITE, HZ/10);
- rpc_clnt_sigunmask(clnt, &oldset);
- if (ret == -ERESTARTSYS)
- break;
- ret = 0;
- } while (bdi_write_congested(bdi));
-
- return ret;
-}
-
/*
* Try to update any existing write request, or create one if there is none.
* In order to match, the request's credentials must match those of
@@ -636,12 +604,10 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
struct inode *inode = mapping->host;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req, *new = NULL;
- unsigned long rqend, end;
+ pgoff_t rqend, end;
end = offset + bytes;
- if (nfs_wait_on_write_congestion(mapping))
- return ERR_PTR(-ERESTARTSYS);
for (;;) {
/* Loop over all inode entries and see if we find
* A request for the page we wish to update
@@ -865,7 +831,7 @@ static void nfs_execute_write(struct nfs_write_data *data)
* Generate multiple small requests to write out a single
* contiguous dirty area on one page.
*/
-static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
+static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
{
struct nfs_page *req = nfs_list_entry(head->next);
struct page *page = req->wb_page;
@@ -877,11 +843,11 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
nfs_list_remove_request(req);
- nbytes = req->wb_bytes;
+ nbytes = count;
do {
size_t len = min(nbytes, wsize);
- data = nfs_writedata_alloc(len);
+ data = nfs_writedata_alloc(1);
if (!data)
goto out_bad;
list_add(&data->pages, &list);
@@ -892,23 +858,19 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
ClearPageError(page);
offset = 0;
- nbytes = req->wb_bytes;
+ nbytes = count;
do {
data = list_entry(list.next, struct nfs_write_data, pages);
list_del_init(&data->pages);
data->pagevec[0] = page;
- if (nbytes > wsize) {
- nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
- wsize, offset, how);
- offset += wsize;
- nbytes -= wsize;
- } else {
- nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
- nbytes, offset, how);
- nbytes = 0;
- }
+ if (nbytes < wsize)
+ wsize = nbytes;
+ nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
+ wsize, offset, how);
+ offset += wsize;
+ nbytes -= wsize;
nfs_execute_write(data);
} while (nbytes != 0);
@@ -934,26 +896,23 @@ out_bad:
* This is the case if nfs_updatepage detects a conflicting request
* that has been written but not committed.
*/
-static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
+static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
{
struct nfs_page *req;
struct page **pages;
struct nfs_write_data *data;
- unsigned int count;
- data = nfs_writedata_alloc(NFS_SERVER(inode)->wsize);
+ data = nfs_writedata_alloc(npages);
if (!data)
goto out_bad;
pages = data->pagevec;
- count = 0;
while (!list_empty(head)) {
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_list_add_request(req, &data->pages);
ClearPageError(req->wb_page);
*pages++ = req->wb_page;
- count += req->wb_bytes;
}
req = nfs_list_entry(data->pages.next);
@@ -973,40 +932,15 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
return -ENOMEM;
}
-static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how)
+static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
+ struct inode *inode, int ioflags)
{
- LIST_HEAD(one_request);
- int (*flush_one)(struct inode *, struct list_head *, int);
- struct nfs_page *req;
- int wpages = NFS_SERVER(inode)->wpages;
int wsize = NFS_SERVER(inode)->wsize;
- int error;
- flush_one = nfs_flush_one;
if (wsize < PAGE_CACHE_SIZE)
- flush_one = nfs_flush_multi;
- /* For single writes, FLUSH_STABLE is more efficient */
- if (npages <= wpages && npages == NFS_I(inode)->npages
- && nfs_list_entry(head->next)->wb_bytes <= wsize)
- how |= FLUSH_STABLE;
-
- do {
- nfs_coalesce_requests(head, &one_request, wpages);
- req = nfs_list_entry(one_request.next);
- error = flush_one(inode, &one_request, how);
- if (error < 0)
- goto out_err;
- } while (!list_empty(head));
- return 0;
-out_err:
- while (!list_empty(head)) {
- req = nfs_list_entry(head->next);
- nfs_list_remove_request(req);
- nfs_redirty_request(req);
- nfs_end_page_writeback(req->wb_page);
- nfs_clear_page_writeback(req);
- }
- return error;
+ nfs_pageio_init(pgio, inode, nfs_flush_multi, wsize, ioflags);
+ else
+ nfs_pageio_init(pgio, inode, nfs_flush_one, wsize, ioflags);
}
/*
@@ -1330,31 +1264,7 @@ static const struct rpc_call_ops nfs_commit_ops = {
.rpc_call_done = nfs_commit_done,
.rpc_release = nfs_commit_release,
};
-#else
-static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how)
-{
- return 0;
-}
-#endif
-
-static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how)
-{
- struct nfs_inode *nfsi = NFS_I(mapping->host);
- LIST_HEAD(head);
- long res;
-
- spin_lock(&nfsi->req_lock);
- res = nfs_scan_dirty(mapping, wbc, &head);
- spin_unlock(&nfsi->req_lock);
- if (res) {
- int error = nfs_flush_list(mapping->host, &head, res, how);
- if (error < 0)
- return error;
- }
- return res;
-}
-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
int nfs_commit_inode(struct inode *inode, int how)
{
struct nfs_inode *nfsi = NFS_I(inode);
@@ -1371,13 +1281,18 @@ int nfs_commit_inode(struct inode *inode, int how)
}
return res;
}
+#else
+static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how)
+{
+ return 0;
+}
#endif
long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how)
{
struct inode *inode = mapping->host;
struct nfs_inode *nfsi = NFS_I(inode);
- unsigned long idx_start, idx_end;
+ pgoff_t idx_start, idx_end;
unsigned int npages = 0;
LIST_HEAD(head);
int nocommit = how & FLUSH_NOCOMMIT;
@@ -1390,41 +1305,24 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr
idx_start = wbc->range_start >> PAGE_CACHE_SHIFT;
idx_end = wbc->range_end >> PAGE_CACHE_SHIFT;
if (idx_end > idx_start) {
- unsigned long l_npages = 1 + idx_end - idx_start;
+ pgoff_t l_npages = 1 + idx_end - idx_start;
npages = l_npages;
if (sizeof(npages) != sizeof(l_npages) &&
- (unsigned long)npages != l_npages)
+ (pgoff_t)npages != l_npages)
npages = 0;
}
}
how &= ~FLUSH_NOCOMMIT;
spin_lock(&nfsi->req_lock);
do {
- wbc->pages_skipped = 0;
ret = nfs_wait_on_requests_locked(inode, idx_start, npages);
if (ret != 0)
continue;
- pages = nfs_scan_dirty(mapping, wbc, &head);
- if (pages != 0) {
- spin_unlock(&nfsi->req_lock);
- if (how & FLUSH_INVALIDATE) {
- nfs_cancel_dirty_list(&head);
- ret = pages;
- } else
- ret = nfs_flush_list(inode, &head, pages, how);
- spin_lock(&nfsi->req_lock);
- continue;
- }
- if (wbc->pages_skipped != 0)
- continue;
if (nocommit)
break;
pages = nfs_scan_commit(inode, &head, idx_start, npages);
- if (pages == 0) {
- if (wbc->pages_skipped != 0)
- continue;
+ if (pages == 0)
break;
- }
if (how & FLUSH_INVALIDATE) {
spin_unlock(&nfsi->req_lock);
nfs_cancel_commit_list(&head);
@@ -1456,7 +1354,7 @@ int nfs_wb_all(struct inode *inode)
};
int ret;
- ret = generic_writepages(mapping, &wbc);
+ ret = nfs_writepages(mapping, &wbc);
if (ret < 0)
goto out;
ret = nfs_sync_mapping_wait(mapping, &wbc, 0);
@@ -1479,11 +1377,9 @@ int nfs_sync_mapping_range(struct address_space *mapping, loff_t range_start, lo
};
int ret;
- if (!(how & FLUSH_NOWRITEPAGE)) {
- ret = generic_writepages(mapping, &wbc);
- if (ret < 0)
- goto out;
- }
+ ret = nfs_writepages(mapping, &wbc);
+ if (ret < 0)
+ goto out;
ret = nfs_sync_mapping_wait(mapping, &wbc, how);
if (ret >= 0)
return 0;
@@ -1506,7 +1402,7 @@ int nfs_wb_page_priority(struct inode *inode, struct page *page, int how)
int ret;
BUG_ON(!PageLocked(page));
- if (!(how & FLUSH_NOWRITEPAGE) && clear_page_dirty_for_io(page)) {
+ if (clear_page_dirty_for_io(page)) {
ret = nfs_writepage_locked(page, &wbc);
if (ret < 0)
goto out;
@@ -1531,10 +1427,18 @@ int nfs_wb_page(struct inode *inode, struct page* page)
int nfs_set_page_dirty(struct page *page)
{
- spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock;
+ struct address_space *mapping = page->mapping;
+ struct inode *inode;
+ spinlock_t *req_lock;
struct nfs_page *req;
int ret;
+ if (!mapping)
+ goto out_raced;
+ inode = mapping->host;
+ if (!inode)
+ goto out_raced;
+ req_lock = &NFS_I(inode)->req_lock;
spin_lock(req_lock);
req = nfs_page_find_request_locked(page);
if (req != NULL) {
@@ -1547,6 +1451,8 @@ int nfs_set_page_dirty(struct page *page)
ret = __set_page_dirty_nobuffers(page);
spin_unlock(req_lock);
return ret;
+out_raced:
+ return !TestSetPageDirty(page);
}
OpenPOWER on IntegriCloud