aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@fys.uio.no>2005-01-05 02:17:13 +0100
committerTrond Myklebust <trond.myklebust@fys.uio.no>2005-01-05 02:17:13 +0100
commitca486f406c8c180c964dbe52a4d36429f4865d40 (patch)
tree0b7461d72e49a4b722e4cae21609adf151d0b523 /fs
parente83235934b015fa6cd32736bb898cfb58c6d7a1a (diff)
parent9e84df776f2ce9cd66e51cc55afc821468597556 (diff)
downloadhistory-ca486f406c8c180c964dbe52a4d36429f4865d40.tar.gz
Merge bk://nfsclient.bkbits.net/linux-2.6
into fys.uio.no:/home/linux/bitkeeper/nfsclient-2.6
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c48
-rw-r--r--fs/locks.c6
-rw-r--r--fs/nfs/dir.c225
-rw-r--r--fs/nfs/direct.c425
-rw-r--r--fs/nfs/file.c50
-rw-r--r--fs/nfs/inode.c107
-rw-r--r--fs/nfs/nfs3proc.c20
-rw-r--r--fs/nfs/nfs4proc.c189
-rw-r--r--fs/nfs/nfs4state.c10
-rw-r--r--fs/nfs/proc.c16
-rw-r--r--fs/nfs/read.c19
-rw-r--r--fs/nfs/unlink.c3
-rw-r--r--fs/nfs/write.c48
13 files changed, 662 insertions, 504 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 500c4c4d02f49d..bd8f593a3a7dc8 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -784,6 +784,54 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
}
/**
+ * d_instantiate_unique - instantiate a non-aliased dentry
+ * @entry: dentry to instantiate
+ * @inode: inode to attach to this dentry
+ *
+ * Fill in inode information in the entry. On success, it returns NULL.
+ * If an unhashed alias of "entry" already exists, then we return the
+ * aliased dentry instead.
+ *
+ * Note that in order to avoid conflicts with rename() etc, the caller
+ * had better be holding the parent directory semaphore.
+ */
+struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
+{
+ struct dentry *alias;
+ int len = entry->d_name.len;
+ const char *name = entry->d_name.name;
+ unsigned int hash = entry->d_name.hash;
+
+ BUG_ON(!list_empty(&entry->d_alias));
+ spin_lock(&dcache_lock);
+ if (!inode)
+ goto do_negative;
+ list_for_each_entry(alias, &inode->i_dentry, d_alias) {
+ struct qstr *qstr = &alias->d_name;
+
+ if (qstr->hash != hash)
+ continue;
+ if (alias->d_parent != entry->d_parent)
+ continue;
+ if (qstr->len != len)
+ continue;
+ if (memcmp(qstr->name, name, len))
+ continue;
+ dget_locked(alias);
+ spin_unlock(&dcache_lock);
+ BUG_ON(!d_unhashed(alias));
+ return alias;
+ }
+ list_add(&entry->d_alias, &inode->i_dentry);
+do_negative:
+ entry->d_inode = inode;
+ spin_unlock(&dcache_lock);
+ security_d_instantiate(entry, inode);
+ return NULL;
+}
+EXPORT_SYMBOL(d_instantiate_unique);
+
+/**
* d_alloc_root - allocate root dentry
* @root_inode: inode to allocate the root for
*
diff --git a/fs/locks.c b/fs/locks.c
index fd6e88bb9890d6..1a8c578e7ccd45 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1563,9 +1563,6 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
goto out;
- else if (error == LOCK_USE_CLNT)
- /* Bypass for NFS with no locking - 2.0.36 compat */
- fl = posix_test_lock(filp, &file_lock);
else
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
@@ -1708,9 +1705,6 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
goto out;
- else if (error == LOCK_USE_CLNT)
- /* Bypass for NFS with no locking - 2.0.36 compat */
- fl = posix_test_lock(filp, &file_lock);
else
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index eac30582ff9e9a..0e4e9da69f44b6 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -40,8 +40,6 @@
static int nfs_opendir(struct inode *, struct file *);
static int nfs_readdir(struct file *, void *, filldir_t);
static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *);
-static int nfs_cached_lookup(struct inode *, struct dentry *,
- struct nfs_fh *, struct nfs_fattr *);
static int nfs_create(struct inode *, struct dentry *, int, struct nameidata *);
static int nfs_mkdir(struct inode *, struct dentry *, int);
static int nfs_rmdir(struct inode *, struct dentry *);
@@ -294,24 +292,13 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
return res;
}
-static unsigned int nfs_type2dtype[] = {
- DT_UNKNOWN,
- DT_REG,
- DT_DIR,
- DT_BLK,
- DT_CHR,
- DT_LNK,
- DT_SOCK,
- DT_UNKNOWN,
- DT_FIFO
-};
-
-static inline
-unsigned int nfs_type_to_d_type(enum nfs_ftype type)
+static inline unsigned int dt_type(struct inode *inode)
{
- return nfs_type2dtype[type];
+ return (inode->i_mode >> 12) & 15;
}
+static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc);
+
/*
* Once we've found the start of the dirent within a page: fill 'er up...
*/
@@ -321,6 +308,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
{
struct file *file = desc->file;
struct nfs_entry *entry = desc->entry;
+ struct dentry *dentry = NULL;
unsigned long fileid;
int loop_count = 0,
res;
@@ -333,9 +321,16 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
* retrieving the current dirent on the server */
fileid = nfs_fileid_to_ino_t(entry->ino);
+ /* Get a dentry if we have one */
+ if (dentry != NULL)
+ dput(dentry);
+ dentry = nfs_readdir_lookup(desc);
+
/* Use readdirplus info */
- if (desc->plus && (entry->fattr->valid & NFS_ATTR_FATTR))
- d_type = nfs_type_to_d_type(entry->fattr->type);
+ if (dentry != NULL && dentry->d_inode != NULL) {
+ d_type = dt_type(dentry->d_inode);
+ fileid = dentry->d_inode->i_ino;
+ }
res = filldir(dirent, entry->name, entry->len,
entry->prev_cookie, fileid, d_type);
@@ -352,7 +347,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
}
}
dir_page_release(desc);
-
+ if (dentry != NULL)
+ dput(dentry);
dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res);
return res;
}
@@ -615,24 +611,10 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
goto out_valid;
}
- /*
- * Note: we're not holding inode->i_sem and so may be racing with
- * operations that change the directory. We therefore save the
- * change attribute *before* we do the RPC call.
- */
- verifier = nfs_save_change_attribute(dir);
- error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
- if (!error) {
- if (nfs_compare_fh(NFS_FH(inode), &fhandle))
- goto out_bad;
- if (nfs_lookup_verify_inode(inode, isopen))
- goto out_zap_parent;
- goto out_valid_renew;
- }
-
if (NFS_STALE(inode))
goto out_bad;
+ verifier = nfs_save_change_attribute(dir);
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error)
goto out_bad;
@@ -641,7 +623,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad;
- out_valid_renew:
nfs_renew_times(dentry);
nfs_set_verifier(dentry, verifier);
out_valid:
@@ -723,6 +704,7 @@ int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd)
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
+ struct dentry *res;
struct inode *inode = NULL;
int error;
struct nfs_fh fhandle;
@@ -731,11 +713,11 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
dfprintk(VFS, "NFS: lookup(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- error = -ENAMETOOLONG;
+ res = ERR_PTR(-ENAMETOOLONG);
if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
goto out;
- error = -ENOMEM;
+ res = ERR_PTR(-ENOMEM);
dentry->d_op = NFS_PROTO(dir)->dentry_ops;
lock_kernel();
@@ -746,29 +728,27 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
if (nfs_is_exclusive_create(dir, nd))
goto no_entry;
- error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
- if (error != 0) {
- error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name,
- &fhandle, &fattr);
- if (error == -ENOENT)
- goto no_entry;
- if (error != 0)
- goto out_unlock;
+ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
+ if (error == -ENOENT)
+ goto no_entry;
+ if (error < 0) {
+ res = ERR_PTR(error);
+ goto out_unlock;
}
- error = -EACCES;
+ res = ERR_PTR(-EACCES);
inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
if (!inode)
goto out_unlock;
no_entry:
- error = 0;
- d_add(dentry, inode);
+ res = d_add_unique(dentry, inode);
+ if (res != NULL)
+ dentry = res;
nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_unlock:
unlock_kernel();
out:
- BUG_ON(error > 0);
- return ERR_PTR(error);
+ return res;
}
#ifdef CONFIG_NFS_V4
@@ -798,15 +778,15 @@ static int is_atomic_open(struct inode *dir, struct nameidata *nd)
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
+ struct dentry *res = NULL;
struct inode *inode = NULL;
- int error = 0;
/* Check that we are indeed trying to open this file */
if (!is_atomic_open(dir, nd))
goto no_open;
if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
- error = -ENAMETOOLONG;
+ res = ERR_PTR(-ENAMETOOLONG);
goto out;
}
dentry->d_op = NFS_PROTO(dir)->dentry_ops;
@@ -828,7 +808,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
inode = nfs4_atomic_open(dir, dentry, nd);
unlock_kernel();
if (IS_ERR(inode)) {
- error = PTR_ERR(inode);
+ int error = PTR_ERR(inode);
switch (error) {
/* Make a negative dentry */
case -ENOENT:
@@ -841,16 +821,18 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
/* case -EISDIR: */
/* case -EINVAL: */
default:
+ res = ERR_PTR(error);
goto out;
}
}
no_entry:
- d_add(dentry, inode);
+ res = d_add_unique(dentry, inode);
+ if (res != NULL)
+ dentry = res;
nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out:
- BUG_ON(error > 0);
- return ERR_PTR(error);
+ return res;
no_open:
return nfs_lookup(dir, dentry, nd);
}
@@ -906,83 +888,51 @@ no_open:
}
#endif /* CONFIG_NFSV4 */
-static inline
-int find_dirent_name(nfs_readdir_descriptor_t *desc, struct page *page, struct dentry *dentry)
+static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
{
+ struct dentry *parent = desc->file->f_dentry;
+ struct inode *dir = parent->d_inode;
struct nfs_entry *entry = desc->entry;
- int status;
-
- while((status = dir_decode(desc)) == 0) {
- if (entry->len != dentry->d_name.len)
- continue;
- if (memcmp(entry->name, dentry->d_name.name, entry->len))
- continue;
- if (!(entry->fattr->valid & NFS_ATTR_FATTR))
- continue;
- break;
- }
- return status;
-}
-
-/*
- * Use the cached Readdirplus results in order to avoid a LOOKUP call
- * whenever we believe that the parent directory has not changed.
- *
- * We assume that any file creation/rename changes the directory mtime.
- * As this results in a page cache invalidation whenever it occurs,
- * we don't require any other tests for cache coherency.
- */
-static
-int nfs_cached_lookup(struct inode *dir, struct dentry *dentry,
- struct nfs_fh *fh, struct nfs_fattr *fattr)
-{
- nfs_readdir_descriptor_t desc;
- struct nfs_server *server;
- struct nfs_entry entry;
- struct page *page;
- unsigned long timestamp;
- int res;
-
- if (!NFS_USE_READDIRPLUS(dir))
- return -ENOENT;
- server = NFS_SERVER(dir);
- /* Don't use readdirplus unless the cache is stable */
- if ((server->flags & NFS_MOUNT_NOAC) != 0
- || nfs_caches_unstable(dir)
- || nfs_attribute_timeout(dir))
- return -ENOENT;
- if ((NFS_FLAGS(dir) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0)
- return -ENOENT;
- timestamp = NFS_I(dir)->readdir_timestamp;
-
- entry.fh = fh;
- entry.fattr = fattr;
-
- desc.decode = NFS_PROTO(dir)->decode_dirent;
- desc.entry = &entry;
- desc.page_index = 0;
- desc.plus = 1;
-
- for(;(page = find_get_page(dir->i_mapping, desc.page_index)); desc.page_index++) {
-
- res = -EIO;
- if (PageUptodate(page)) {
- void * kaddr = kmap_atomic(page, KM_USER0);
- desc.ptr = kaddr;
- res = find_dirent_name(&desc, page, dentry);
- kunmap_atomic(kaddr, KM_USER0);
- }
- page_cache_release(page);
+ struct dentry *dentry, *alias;
+ struct qstr name = {
+ .name = entry->name,
+ .len = entry->len,
+ };
+ struct inode *inode;
- if (res == 0)
- goto out_found;
- if (res != -EAGAIN)
+ switch (name.len) {
+ case 2:
+ if (name.name[0] == '.' && name.name[1] == '.')
+ return dget_parent(parent);
break;
+ case 1:
+ if (name.name[0] == '.')
+ return dget(parent);
}
- return -ENOENT;
- out_found:
- fattr->timestamp = timestamp;
- return 0;
+ name.hash = full_name_hash(name.name, name.len);
+ dentry = d_lookup(parent, &name);
+ if (dentry != NULL)
+ return dentry;
+ if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR))
+ return NULL;
+ /* Note: caller is already holding the dir->i_sem! */
+ dentry = d_alloc(parent, &name);
+ if (dentry == NULL)
+ return NULL;
+ dentry->d_op = NFS_PROTO(dir)->dentry_ops;
+ inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+ alias = d_add_unique(dentry, inode);
+ if (alias != NULL) {
+ dput(dentry);
+ dentry = alias;
+ }
+ nfs_renew_times(dentry);
+ nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+ return dentry;
}
/*
@@ -1045,15 +995,9 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
if (nd && (nd->flags & LOOKUP_CREATE))
open_flags = nd->intent.open.flags;
- /*
- * The 0 argument passed into the create function should one day
- * contain the O_EXCL flag if requested. This allows NFSv3 to
- * select the appropriate create strategy. Currently open_namei
- * does not pass the create flags.
- */
lock_kernel();
nfs_begin_data_update(dir);
- inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags);
+ inode = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
nfs_end_data_update(dir);
if (!IS_ERR(inode)) {
d_instantiate(dentry, inode);
@@ -1438,7 +1382,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto go_ahead;
if (S_ISDIR(new_inode->i_mode))
goto out;
- else if (atomic_read(&new_dentry->d_count) > 1) {
+ else if (atomic_read(&new_dentry->d_count) > 2) {
int err;
/* copy the target dentry's name */
dentry = d_alloc(new_dentry->d_parent,
@@ -1453,10 +1397,8 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_inode = NULL;
/* instantiate the replacement target */
d_instantiate(new_dentry, NULL);
- }
-
+ } else if (atomic_read(&new_dentry->d_count) > 1) {
/* dentry still busy? */
- if (atomic_read(&new_dentry->d_count) > 1) {
#ifdef NFS_PARANOIA
printk("nfs_rename: target %s/%s busy, d_count=%d\n",
new_dentry->d_parent->d_name.name,
@@ -1510,7 +1452,7 @@ int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs
if (cache->cred != cred
|| time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
- || (NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR))
+ || (NFS_FLAGS(inode) & NFS_INO_INVALID_ACCESS))
return -ENOENT;
memcpy(res, cache, sizeof(*res));
return 0;
@@ -1524,6 +1466,7 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = get_rpccred(set->cred);
+ NFS_FLAGS(inode) &= ~NFS_INO_INVALID_ACCESS;
}
cache->jiffies = set->jiffies;
cache->mask = set->mask;
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index d6862fe4df4bfb..6a2f9901f8cb2d 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -33,6 +33,7 @@
* 08 Jul 2002 Version for 2.4.19, with bug fixes --trondmy
* 08 Jun 2003 Port to 2.5 APIs --cel
* 31 Mar 2004 Handle direct I/O without VFS support --cel
+ * 15 Sep 2004 Parallel async reads --cel
*
*/
@@ -43,6 +44,7 @@
#include <linux/smp_lock.h>
#include <linux/file.h>
#include <linux/pagemap.h>
+#include <linux/kref.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
@@ -50,11 +52,27 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#include <asm/atomic.h>
#define NFSDBG_FACILITY NFSDBG_VFS
-#define VERF_SIZE (2 * sizeof(__u32))
#define MAX_DIRECTIO_SIZE (4096UL << PAGE_SHIFT)
+static kmem_cache_t *nfs_direct_cachep;
+
+/*
+ * This represents a set of asynchronous requests that we're waiting on
+ */
+struct nfs_direct_req {
+ struct kref kref; /* release manager */
+ struct list_head list; /* nfs_read_data structs */
+ wait_queue_head_t wait; /* wait for i/o completion */
+ struct page ** pages; /* pages in our buffer */
+ unsigned int npages; /* count of pages */
+ atomic_t complete, /* i/os we're waiting for */
+ count, /* bytes actually processed */
+ error; /* any reported error */
+};
+
/**
* nfs_get_user_pages - find and set up pages underlying user's buffer
@@ -71,7 +89,8 @@ nfs_get_user_pages(int rw, unsigned long user_addr, size_t size,
unsigned long page_count;
size_t array_size;
- /* set an arbitrary limit to prevent arithmetic overflow */
+ /* set an arbitrary limit to prevent type overflow */
+ /* XXX: this can probably be as large as INT_MAX */
if (size > MAX_DIRECTIO_SIZE)
return -EFBIG;
@@ -93,6 +112,8 @@ nfs_get_user_pages(int rw, unsigned long user_addr, size_t size,
/**
* nfs_free_user_pages - tear down page struct array
* @pages: array of page struct pointers underlying target buffer
+ * @npages: number of pages in the array
+ * @do_dirty: dirty the pages as we release them
*/
static void
nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
@@ -107,77 +128,231 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
}
/**
- * nfs_direct_read_seg - Read in one iov segment. Generate separate
- * read RPCs for each "rsize" bytes.
- * @inode: target inode
- * @ctx: target file open context
- * user_addr: starting address of this segment of user's buffer
- * count: size of this segment
- * file_offset: offset in file to begin the operation
- * @pages: array of addresses of page structs defining user's buffer
- * nr_pages: size of pages array
+ * nfs_direct_req_release - release nfs_direct_req structure for direct read
+ * @kref: kref object embedded in an nfs_direct_req structure
+ *
*/
-static int
-nfs_direct_read_seg(struct inode *inode, struct nfs_open_context *ctx,
- unsigned long user_addr, size_t count, loff_t file_offset,
- struct page **pages, int nr_pages)
+static void nfs_direct_req_release(struct kref *kref)
{
- const unsigned int rsize = NFS_SERVER(inode)->rsize;
- int tot_bytes = 0;
- int curpage = 0;
- struct nfs_read_data rdata = {
- .inode = inode,
- .cred = ctx->cred,
- .args = {
- .fh = NFS_FH(inode),
- .context = ctx,
- },
- .res = {
- .fattr = &rdata.fattr,
- },
- };
+ struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
+ kmem_cache_free(nfs_direct_cachep, dreq);
+}
+
+/**
+ * nfs_direct_read_alloc - allocate nfs_read_data structures for direct read
+ * @count: count of bytes for the read request
+ * @rsize: local rsize setting
+ *
+ * Note we also set the number of requests we have in the dreq when we are
+ * done. This prevents races with I/O completion so we will always wait
+ * until all requests have been dispatched and completed.
+ */
+static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, unsigned int rsize)
+{
+ struct list_head *list;
+ struct nfs_direct_req *dreq;
+ unsigned int reads = 0;
+
+ dreq = kmem_cache_alloc(nfs_direct_cachep, SLAB_KERNEL);
+ if (!dreq)
+ return NULL;
+
+ kref_init(&dreq->kref);
+ init_waitqueue_head(&dreq->wait);
+ INIT_LIST_HEAD(&dreq->list);
+ atomic_set(&dreq->count, 0);
+ atomic_set(&dreq->error, 0);
+
+ list = &dreq->list;
+ for(;;) {
+ struct nfs_read_data *data = nfs_readdata_alloc();
+
+ if (unlikely(!data)) {
+ while (!list_empty(list)) {
+ data = list_entry(list->next,
+ struct nfs_read_data, pages);
+ list_del(&data->pages);
+ nfs_readdata_free(data);
+ }
+ kref_put(&dreq->kref, nfs_direct_req_release);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&data->pages);
+ list_add(&data->pages, list);
- rdata.args.pgbase = user_addr & ~PAGE_MASK;
- rdata.args.offset = file_offset;
- do {
- int result;
+ data->req = (struct nfs_page *) dreq;
+ reads++;
+ if (nbytes <= rsize)
+ break;
+ nbytes -= rsize;
+ }
+ kref_get(&dreq->kref);
+ atomic_set(&dreq->complete, reads);
+ return dreq;
+}
+
+/**
+ * nfs_direct_read_result - handle a read reply for a direct read request
+ * @data: address of NFS READ operation control block
+ * @status: status of this NFS READ operation
+ *
+ * We must hold a reference to all the pages in this direct read request
+ * until the RPCs complete. This could be long *after* we are woken up in
+ * nfs_direct_read_wait (for instance, if someone hits ^C on a slow server).
+ */
+static void nfs_direct_read_result(struct nfs_read_data *data, int status)
+{
+ struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
- rdata.args.count = count;
- if (rdata.args.count > rsize)
- rdata.args.count = rsize;
- rdata.args.pages = &pages[curpage];
+ if (likely(status >= 0))
+ atomic_add(data->res.count, &dreq->count);
+ else
+ atomic_set(&dreq->error, status);
- dprintk("NFS: direct read: c=%u o=%Ld ua=%lu, pb=%u, cp=%u\n",
- rdata.args.count, (long long) rdata.args.offset,
- user_addr + tot_bytes, rdata.args.pgbase, curpage);
+ if (unlikely(atomic_dec_and_test(&dreq->complete))) {
+ nfs_free_user_pages(dreq->pages, dreq->npages, 1);
+ wake_up(&dreq->wait);
+ kref_put(&dreq->kref, nfs_direct_req_release);
+ }
+}
+
+/**
+ * nfs_direct_read_schedule - dispatch NFS READ operations for a direct read
+ * @dreq: address of nfs_direct_req struct for this request
+ * @inode: target inode
+ * @ctx: target file open context
+ * @user_addr: starting address of this segment of user's buffer
+ * @count: size of this segment
+ * @file_offset: offset in file to begin the operation
+ *
+ * For each nfs_read_data struct that was allocated on the list, dispatch
+ * an NFS READ operation
+ */
+static void nfs_direct_read_schedule(struct nfs_direct_req *dreq,
+ struct inode *inode, struct nfs_open_context *ctx,
+ unsigned long user_addr, size_t count, loff_t file_offset)
+{
+ struct list_head *list = &dreq->list;
+ struct page **pages = dreq->pages;
+ unsigned int curpage, pgbase;
+ unsigned int rsize = NFS_SERVER(inode)->rsize;
+
+ curpage = 0;
+ pgbase = user_addr & ~PAGE_MASK;
+ do {
+ struct nfs_read_data *data;
+ unsigned int bytes;
+
+ bytes = rsize;
+ if (count < rsize)
+ bytes = count;
+
+ data = list_entry(list->next, struct nfs_read_data, pages);
+ list_del_init(&data->pages);
+
+ data->inode = inode;
+ data->cred = ctx->cred;
+ data->args.fh = NFS_FH(inode);
+ data->args.context = ctx;
+ data->args.offset = file_offset;
+ data->args.pgbase = pgbase;
+ data->args.pages = &pages[curpage];
+ data->args.count = bytes;
+ data->res.fattr = &data->fattr;
+ data->res.eof = 0;
+ data->res.count = bytes;
+
+ NFS_PROTO(inode)->read_setup(data);
+
+ data->task.tk_cookie = (unsigned long) inode;
+ data->task.tk_calldata = data;
+ data->task.tk_release = nfs_readdata_release;
+ data->complete = nfs_direct_read_result;
lock_kernel();
- result = NFS_PROTO(inode)->read(&rdata);
+ rpc_execute(&data->task);
unlock_kernel();
- if (result <= 0) {
- if (tot_bytes > 0)
- break;
- if (result == -EISDIR)
- result = -EINVAL;
- return result;
- }
+ dfprintk(VFS, "NFS: %4d initiated direct read call (req %s/%Ld, %u bytes @ offset %Lu)\n",
+ data->task.tk_pid,
+ inode->i_sb->s_id,
+ (long long)NFS_FILEID(inode),
+ bytes,
+ (unsigned long long)data->args.offset);
- tot_bytes += result;
- if (rdata.res.eof)
- break;
+ file_offset += bytes;
+ pgbase += bytes;
+ curpage += pgbase >> PAGE_SHIFT;
+ pgbase &= ~PAGE_MASK;
- rdata.args.offset += result;
- rdata.args.pgbase += result;
- curpage += rdata.args.pgbase >> PAGE_SHIFT;
- rdata.args.pgbase &= ~PAGE_MASK;
- count -= result;
+ count -= bytes;
} while (count != 0);
+}
+
+/**
+ * nfs_direct_read_wait - wait for I/O completion for direct reads
+ * @dreq: request on which we are to wait
+ * @intr: whether or not this wait can be interrupted
+ *
+ * Collects and returns the final error value/byte-count.
+ */
+static ssize_t nfs_direct_read_wait(struct nfs_direct_req *dreq, int intr)
+{
+ int result = 0;
+
+ if (intr) {
+ result = wait_event_interruptible(dreq->wait,
+ (atomic_read(&dreq->complete) == 0));
+ } else {
+ wait_event(dreq->wait, (atomic_read(&dreq->complete) == 0));
+ }
- /* XXX: should we zero the rest of the user's buffer if we
- * hit eof? */
+ if (!result)
+ result = atomic_read(&dreq->error);
+ if (!result)
+ result = atomic_read(&dreq->count);
- return tot_bytes;
+ kref_put(&dreq->kref, nfs_direct_req_release);
+ return (ssize_t) result;
+}
+
+/**
+ * nfs_direct_read_seg - Read in one iov segment. Generate separate
+ * read RPCs for each "rsize" bytes.
+ * @inode: target inode
+ * @ctx: target file open context
+ * @user_addr: starting address of this segment of user's buffer
+ * @count: size of this segment
+ * @file_offset: offset in file to begin the operation
+ * @pages: array of addresses of page structs defining user's buffer
+ * @nr_pages: number of pages in the array
+ *
+ */
+static ssize_t nfs_direct_read_seg(struct inode *inode,
+ struct nfs_open_context *ctx, unsigned long user_addr,
+ size_t count, loff_t file_offset, struct page **pages,
+ unsigned int nr_pages)
+{
+ ssize_t result;
+ sigset_t oldset;
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ struct nfs_direct_req *dreq;
+
+ dreq = nfs_direct_read_alloc(count, NFS_SERVER(inode)->rsize);
+ if (!dreq)
+ return -ENOMEM;
+
+ dreq->pages = pages;
+ dreq->npages = nr_pages;
+
+ rpc_clnt_sigmask(clnt, &oldset);
+ nfs_direct_read_schedule(dreq, inode, ctx, user_addr, count,
+ file_offset);
+ result = nfs_direct_read_wait(dreq, clnt->cl_intr);
+ rpc_clnt_sigunmask(clnt, &oldset);
+
+ return result;
}
/**
@@ -189,9 +364,8 @@ nfs_direct_read_seg(struct inode *inode, struct nfs_open_context *ctx,
* file_offset: offset in file to begin the operation
* nr_segs: size of iovec array
*
- * generic_file_direct_IO has already pushed out any non-direct
- * writes so that this read will see them when we read from the
- * server.
+ * We've already pushed out any non-direct writes so that this read
+ * will see them when we read from the server.
*/
static ssize_t
nfs_direct_read(struct inode *inode, struct nfs_open_context *ctx,
@@ -220,8 +394,6 @@ nfs_direct_read(struct inode *inode, struct nfs_open_context *ctx,
result = nfs_direct_read_seg(inode, ctx, user_addr, size,
file_offset, pages, page_count);
- nfs_free_user_pages(pages, page_count, 1);
-
if (result <= 0) {
if (tot_bytes > 0)
break;
@@ -247,31 +419,31 @@ nfs_direct_read(struct inode *inode, struct nfs_open_context *ctx,
* @pages: array of addresses of page structs defining user's buffer
* nr_pages: size of pages array
*/
-static int
-nfs_direct_write_seg(struct inode *inode, struct nfs_open_context *ctx,
- unsigned long user_addr, size_t count, loff_t file_offset,
- struct page **pages, int nr_pages)
+static ssize_t nfs_direct_write_seg(struct inode *inode,
+ struct nfs_open_context *ctx, unsigned long user_addr,
+ size_t count, loff_t file_offset, struct page **pages,
+ int nr_pages)
{
const unsigned int wsize = NFS_SERVER(inode)->wsize;
size_t request;
- int curpage, need_commit, result, tot_bytes;
+ int curpage, need_commit;
+ ssize_t result, tot_bytes;
struct nfs_writeverf first_verf;
- struct nfs_write_data wdata = {
- .inode = inode,
- .cred = ctx->cred,
- .args = {
- .fh = NFS_FH(inode),
- .context = ctx,
- },
- .res = {
- .fattr = &wdata.fattr,
- .verf = &wdata.verf,
- },
- };
+ struct nfs_write_data *wdata;
- wdata.args.stable = NFS_UNSTABLE;
+ wdata = nfs_writedata_alloc();
+ if (!wdata)
+ return -ENOMEM;
+
+ wdata->inode = inode;
+ wdata->cred = ctx->cred;
+ wdata->args.fh = NFS_FH(inode);
+ wdata->args.context = ctx;
+ wdata->args.stable = NFS_UNSTABLE;
if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize)
- wdata.args.stable = NFS_FILE_SYNC;
+ wdata->args.stable = NFS_FILE_SYNC;
+ wdata->res.fattr = &wdata->fattr;
+ wdata->res.verf = &wdata->verf;
nfs_begin_data_update(inode);
retry:
@@ -279,20 +451,20 @@ retry:
tot_bytes = 0;
curpage = 0;
request = count;
- wdata.args.pgbase = user_addr & ~PAGE_MASK;
- wdata.args.offset = file_offset;
- do {
- wdata.args.count = request;
- if (wdata.args.count > wsize)
- wdata.args.count = wsize;
- wdata.args.pages = &pages[curpage];
+ wdata->args.pgbase = user_addr & ~PAGE_MASK;
+ wdata->args.offset = file_offset;
+ do {
+ wdata->args.count = request;
+ if (wdata->args.count > wsize)
+ wdata->args.count = wsize;
+ wdata->args.pages = &pages[curpage];
dprintk("NFS: direct write: c=%u o=%Ld ua=%lu, pb=%u, cp=%u\n",
- wdata.args.count, (long long) wdata.args.offset,
- user_addr + tot_bytes, wdata.args.pgbase, curpage);
+ wdata->args.count, (long long) wdata->args.offset,
+ user_addr + tot_bytes, wdata->args.pgbase, curpage);
lock_kernel();
- result = NFS_PROTO(inode)->write(&wdata);
+ result = NFS_PROTO(inode)->write(wdata);
unlock_kernel();
if (result <= 0) {
@@ -302,20 +474,25 @@ retry:
}
if (tot_bytes == 0)
- memcpy(&first_verf.verifier, &wdata.verf.verifier,
- VERF_SIZE);
- if (wdata.verf.committed != NFS_FILE_SYNC) {
+ memcpy(&first_verf.verifier, &wdata->verf.verifier,
+ sizeof(first_verf.verifier));
+ if (wdata->verf.committed != NFS_FILE_SYNC) {
need_commit = 1;
- if (memcmp(&first_verf.verifier,
- &wdata.verf.verifier, VERF_SIZE))
+ if (memcmp(&first_verf.verifier, &wdata->verf.verifier,
+ sizeof(first_verf.verifier)));
goto sync_retry;
}
- tot_bytes += result;
- wdata.args.offset += result;
- wdata.args.pgbase += result;
- curpage += wdata.args.pgbase >> PAGE_SHIFT;
- wdata.args.pgbase &= ~PAGE_MASK;
+ tot_bytes += result;
+
+ /* in case of a short write: stop now, let the app recover */
+ if (result < wdata->args.count)
+ break;
+
+ wdata->args.offset += result;
+ wdata->args.pgbase += result;
+ curpage += wdata->args.pgbase >> PAGE_SHIFT;
+ wdata->args.pgbase &= ~PAGE_MASK;
request -= result;
} while (request != 0);
@@ -323,27 +500,27 @@ retry:
* Commit data written so far, even in the event of an error
*/
if (need_commit) {
- wdata.args.count = tot_bytes;
- wdata.args.offset = file_offset;
+ wdata->args.count = tot_bytes;
+ wdata->args.offset = file_offset;
lock_kernel();
- result = NFS_PROTO(inode)->commit(&wdata);
+ result = NFS_PROTO(inode)->commit(wdata);
unlock_kernel();
if (result < 0 || memcmp(&first_verf.verifier,
- &wdata.verf.verifier,
- VERF_SIZE) != 0)
+ &wdata->verf.verifier,
+ sizeof(first_verf.verifier)) != 0)
goto sync_retry;
}
result = tot_bytes;
out:
nfs_end_data_update_defer(inode);
-
+ nfs_writedata_free(wdata);
return result;
sync_retry:
- wdata.args.stable = NFS_FILE_SYNC;
+ wdata->args.stable = NFS_FILE_SYNC;
goto retry;
}
@@ -360,9 +537,9 @@ sync_retry:
* that non-direct readers might access, so they will pick up these
* writes immediately.
*/
-static int nfs_direct_write(struct inode *inode, struct nfs_open_context *ctx,
- const struct iovec *iov, loff_t file_offset,
- unsigned long nr_segs)
+static ssize_t nfs_direct_write(struct inode *inode,
+ struct nfs_open_context *ctx, const struct iovec *iov,
+ loff_t file_offset, unsigned long nr_segs)
{
ssize_t tot_bytes = 0;
unsigned long seg = 0;
@@ -502,6 +679,8 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
+ retval = nfs_wb_all(inode);
+ if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
@@ -591,6 +770,8 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
+ retval = nfs_wb_all(inode);
+ if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
@@ -605,3 +786,21 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
out:
return retval;
}
+
+int nfs_init_directcache(void)
+{
+ nfs_direct_cachep = kmem_cache_create("nfs_direct_cache",
+ sizeof(struct nfs_direct_req),
+ 0, SLAB_RECLAIM_ACCOUNT,
+ NULL, NULL);
+ if (nfs_direct_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void nfs_destroy_directcache(void)
+{
+ if (kmem_cache_destroy(nfs_direct_cachep))
+ printk(KERN_INFO "nfs_direct_cache: not all structures were freed\n");
+}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 21071ddd466277..7f1132b472be79 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -295,10 +295,19 @@ out_swapfile:
static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
{
struct inode *inode = filp->f_mapping->host;
- int status;
+ int status = 0;
lock_kernel();
- status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+ /* Use local locking if mounted with "-onolock" */
+ if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
+ status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+ else {
+ struct file_lock *cfl = posix_test_lock(filp, fl);
+ if (cfl != NULL) {
+ memcpy(fl, cfl, sizeof(*fl));
+ fl->fl_type = F_UNLCK;
+ }
+ }
unlock_kernel();
return status;
}
@@ -325,7 +334,11 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
* still need to complete the unlock.
*/
lock_kernel();
- status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+ /* Use local locking if mounted with "-onolock" */
+ if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
+ status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+ else
+ status = posix_lock_file_wait(filp, fl);
rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
return status;
}
@@ -351,15 +364,19 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
return status;
lock_kernel();
- status = NFS_PROTO(inode)->lock(filp, cmd, fl);
- /* If we were signalled we still need to ensure that
- * we clean up any state on the server. We therefore
- * record the lock call as having succeeded in order to
- * ensure that locks_remove_posix() cleans it out when
- * the process exits.
- */
- if (status == -EINTR || status == -ERESTARTSYS)
- posix_lock_file(filp, fl);
+ /* Use local locking if mounted with "-onolock" */
+ if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) {
+ status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+ /* If we were signalled we still need to ensure that
+ * we clean up any state on the server. We therefore
+ * record the lock call as having succeeded in order to
+ * ensure that locks_remove_posix() cleans it out when
+ * the process exits.
+ */
+ if (status == -EINTR || status == -ERESTARTSYS)
+ posix_lock_file(filp, fl);
+ } else
+ status = posix_lock_file_wait(filp, fl);
unlock_kernel();
if (status < 0)
return status;
@@ -396,15 +413,6 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
return -ENOLCK;
- if (NFS_PROTO(inode)->version != 4) {
- /* Fake OK code if mounted without NLM support */
- if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
- if (IS_GETLK(cmd))
- return LOCK_USE_CLNT;
- return 0;
- }
- }
-
/*
* No BSD flocks over NFS allowed.
* Note: we could try to fake a POSIX lock request here by
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index e2188dd7526ce7..8701801966dcd0 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -486,13 +486,27 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf)
if (error < 0)
goto out_err;
- buf->f_frsize = server->wtmult;
+ /*
+ * Current versions of glibc do not correctly handle the
+ * case where f_frsize != f_bsize. Eventually we want to
+ * report the value of wtmult in this field.
+ */
+ buf->f_frsize = sb->s_blocksize;
+
+ /*
+ * On most *nix systems, f_blocks, f_bfree, and f_bavail
+ * are reported in units of f_frsize. Linux hasn't had
+ * an f_frsize field in its statfs struct until recently,
+ * thus historically Linux's sys_statfs reports these
+ * fields in units of f_bsize.
+ */
buf->f_bsize = sb->s_blocksize;
blockbits = sb->s_blocksize_bits;
blockres = (1 << blockbits) - 1;
buf->f_blocks = (res.tbytes + blockres) >> blockbits;
buf->f_bfree = (res.fbytes + blockres) >> blockbits;
buf->f_bavail = (res.abytes + blockres) >> blockbits;
+
buf->f_files = res.tfiles;
buf->f_ffree = res.afiles;
@@ -565,9 +579,9 @@ nfs_zap_caches(struct inode *inode)
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
- nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+ nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
else
- nfsi->flags |= NFS_INO_INVALID_ATTR;
+ nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
}
/*
@@ -605,7 +619,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
return 0;
if (nfs_compare_fh(NFS_FH(inode), fh))
return 0;
- if (is_bad_inode(inode))
+ if (is_bad_inode(inode) || NFS_STALE(inode))
return 0;
return 1;
}
@@ -766,13 +780,8 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
vmtruncate(inode, attr->ia_size);
}
}
- if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
- struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
- if (*cred) {
- put_rpccred(*cred);
- *cred = NULL;
- }
- }
+ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
+ NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS;
nfs_end_data_update(inode);
unlock_kernel();
return error;
@@ -949,14 +958,14 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
lock_kernel();
if (!inode || is_bad_inode(inode))
goto out_nowait;
- if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode)
+ if (NFS_STALE(inode))
goto out_nowait;
while (NFS_REVALIDATING(inode)) {
status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
if (status < 0)
goto out_nowait;
- if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOAC)
+ if (NFS_ATTRTIMEO(inode) == 0)
continue;
if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME))
continue;
@@ -968,14 +977,14 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
/* Protect against RPC races by saving the change attribute */
verifier = nfs_save_change_attribute(inode);
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr);
- if (status) {
+ if (status != 0) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status);
if (status == -ESTALE) {
- NFS_FLAGS(inode) |= NFS_INO_STALE;
- if (inode != inode->i_sb->s_root->d_inode)
- remove_inode_hash(inode);
+ nfs_zap_caches(inode);
+ if (!S_ISDIR(inode->i_mode))
+ NFS_FLAGS(inode) |= NFS_INO_STALE;
}
goto out;
}
@@ -1014,7 +1023,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
- NFS_FLAGS(inode) &= ~NFS_INO_STALE;
out:
NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
wake_up(&nfsi->nfs_i_wait);
@@ -1161,7 +1169,7 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)
|| inode->i_uid != fattr->uid
|| inode->i_gid != fattr->gid)
- nfsi->flags |= NFS_INO_INVALID_ATTR;
+ nfsi->flags |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
/* Has the link count changed? */
if (inode->i_nlink != fattr->nlink)
@@ -1270,7 +1278,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
#endif
nfsi->change_attr = fattr->change_attr;
if (!data_unstable)
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+ invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
}
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
@@ -1278,14 +1286,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) ||
inode->i_uid != fattr->uid ||
- inode->i_gid != fattr->gid) {
- struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
- if (*cred) {
- put_rpccred(*cred);
- *cred = NULL;
- }
- invalid |= NFS_INO_INVALID_ATTR;
- }
+ inode->i_gid != fattr->gid)
+ invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
@@ -1335,7 +1337,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
*/
nfs_invalidate_inode(inode);
out_err:
- return -EIO;
+ NFS_FLAGS(inode) |= NFS_INO_STALE;
+ return -ESTALE;
}
/*
@@ -1449,8 +1452,6 @@ static void nfs_kill_super(struct super_block *s)
kill_anon_super(s);
- nfs4_renewd_prepare_shutdown(server);
-
if (server->client != NULL && !IS_ERR(server->client))
rpc_shutdown_client(server->client);
if (server->client_sys != NULL && !IS_ERR(server->client_sys))
@@ -1461,8 +1462,6 @@ static void nfs_kill_super(struct super_block *s)
rpciod_down(); /* release rpciod */
- destroy_nfsv4_state(server);
-
if (server->hostname != NULL)
kfree(server->hostname);
kfree(server);
@@ -1543,9 +1542,6 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
server->wsize = nfs_block_size(data->wsize, NULL);
server->flags = data->flags & NFS_MOUNT_FLAGMASK;
- /* NFSv4 doesn't use NLM locking */
- server->flags |= NFS_MOUNT_NONLM;
-
server->acregmin = data->acregmin*HZ;
server->acregmax = data->acregmax*HZ;
server->acdirmin = data->acdirmin*HZ;
@@ -1790,8 +1786,22 @@ out_free:
static void nfs4_kill_super(struct super_block *sb)
{
+ struct nfs_server *server = NFS_SB(sb);
+
nfs_return_all_delegations(sb);
- nfs_kill_super(sb);
+ kill_anon_super(sb);
+
+ nfs4_renewd_prepare_shutdown(server);
+
+ if (server->client != NULL && !IS_ERR(server->client))
+ rpc_shutdown_client(server->client);
+ rpciod_down(); /* release rpciod */
+
+ destroy_nfsv4_state(server);
+
+ if (server->hostname != NULL)
+ kfree(server->hostname);
+ kfree(server);
}
static struct file_system_type nfs4_fs_type = {
@@ -1821,9 +1831,13 @@ static struct file_system_type nfs4_fs_type = {
extern int nfs_init_nfspagecache(void);
extern void nfs_destroy_nfspagecache(void);
extern int nfs_init_readpagecache(void);
-extern int nfs_destroy_readpagecache(void);
+extern void nfs_destroy_readpagecache(void);
extern int nfs_init_writepagecache(void);
-extern int nfs_destroy_writepagecache(void);
+extern void nfs_destroy_writepagecache(void);
+#ifdef CONFIG_NFS_DIRECTIO
+extern int nfs_init_directcache(void);
+extern void nfs_destroy_directcache(void);
+#endif
static kmem_cache_t * nfs_inode_cachep;
@@ -1904,6 +1918,12 @@ static int __init init_nfs_fs(void)
if (err)
goto out1;
+#ifdef CONFIG_NFS_DIRECTIO
+ err = nfs_init_directcache();
+ if (err)
+ goto out0;
+#endif
+
#ifdef CONFIG_PROC_FS
rpc_proc_register(&nfs_rpcstat);
#endif
@@ -1914,8 +1934,14 @@ static int __init init_nfs_fs(void)
goto out;
return 0;
out:
+#ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs");
+#endif
nfs_destroy_writepagecache();
+#ifdef CONFIG_NFS_DIRECTIO
+out0:
+ nfs_destroy_directcache();
+#endif
out1:
nfs_destroy_readpagecache();
out2:
@@ -1928,6 +1954,9 @@ out4:
static void __exit exit_nfs_fs(void)
{
+#ifdef CONFIG_NFS_DIRECTIO
+ nfs_destroy_directcache();
+#endif
nfs_destroy_writepagecache();
nfs_destroy_readpagecache();
nfs_destroy_inodecache();
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 6be08f67800917..828945d6834226 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -80,10 +80,10 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
dprintk("%s: call fsinfo\n", __FUNCTION__);
info->fattr->valid = 0;
status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
- dprintk("%s: reply fsinfo %d\n", __FUNCTION__, status);
+ dprintk("%s: reply fsinfo: %d\n", __FUNCTION__, status);
if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
status = rpc_call(server->client_sys, NFS3PROC_GETATTR, fhandle, info->fattr, 0);
- dprintk("%s: reply getattr %d\n", __FUNCTION__, status);
+ dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
}
return status;
}
@@ -101,7 +101,7 @@ nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
fattr->valid = 0;
status = rpc_call(server->client, NFS3PROC_GETATTR,
fhandle, fattr, 0);
- dprintk("NFS reply getattr\n");
+ dprintk("NFS reply getattr: %d\n", status);
return status;
}
@@ -119,7 +119,7 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
dprintk("NFS call setattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0);
- dprintk("NFS reply setattr\n");
+ dprintk("NFS reply setattr: %d\n", status);
return status;
}
@@ -198,7 +198,7 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
- dprintk("NFS reply access, status = %d\n", status);
+ dprintk("NFS reply access: %d\n", status);
return status;
}
@@ -296,7 +296,7 @@ static int nfs3_proc_commit(struct nfs_write_data *cdata)
* For now, we don't implement O_EXCL.
*/
static struct inode *
-nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
+nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
{
struct nfs_fh fhandle;
@@ -304,8 +304,8 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
struct nfs_fattr dir_attr;
struct nfs3_createargs arg = {
.fh = NFS_FH(dir),
- .name = name->name,
- .len = name->len,
+ .name = dentry->d_name.name,
+ .len = dentry->d_name.len,
.sattr = sattr,
};
struct nfs3_diropres res = {
@@ -315,7 +315,7 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
};
int status;
- dprintk("NFS call create %s\n", name->name);
+ dprintk("NFS call create %s\n", dentry->d_name.name);
arg.createmode = NFS3_CREATE_UNCHECKED;
if (flags & O_EXCL) {
arg.createmode = NFS3_CREATE_EXCLUSIVE;
@@ -353,7 +353,7 @@ exit:
if (status != 0)
goto out;
if (fhandle.size == 0 || !(fattr.valid & NFS_ATTR_FATTR)) {
- status = nfs3_proc_lookup(dir, name, &fhandle, &fattr);
+ status = nfs3_proc_lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (status != 0)
goto out;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 7509bd2ae1818f..7d56ca4d63b72e 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -477,7 +477,7 @@ static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, st
/*
* Returns an nfs4_state + an referenced inode
*/
-static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
{
struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
@@ -491,7 +491,7 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.open_flags = flags,
- .name = name,
+ .name = &dentry->d_name,
.server = server,
.bitmask = server->attr_bitmask,
.claim = NFS4_OPEN_CLAIM_NULL,
@@ -581,14 +581,14 @@ out_err:
}
-struct nfs4_state *nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred)
+struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
struct nfs4_state *res;
int status;
do {
- status = _nfs4_do_open(dir, name, flags, sattr, cred, &res);
+ status = _nfs4_do_open(dir, dentry, flags, sattr, cred, &res);
if (status == 0)
break;
/* NOTE: BAD_SEQID means the server and client disagree about the
@@ -635,6 +635,8 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
fattr->valid = 0;
+ if (state != NULL)
+ msg.rpc_cred = state->owner->so_cred;
if (sattr->ia_valid & ATTR_SIZE)
nfs4_copy_stateid(&arg.stateid, state, NULL);
else
@@ -658,6 +660,61 @@ int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
return err;
}
+struct nfs4_closedata {
+ struct inode *inode;
+ struct nfs4_state *state;
+ struct nfs_closeargs arg;
+ struct nfs_closeres res;
+};
+
+static void nfs4_close_done(struct rpc_task *task)
+{
+ struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
+ struct nfs4_state *state = calldata->state;
+ struct nfs4_state_owner *sp = state->owner;
+ struct nfs_server *server = NFS_SERVER(calldata->inode);
+
+ /* hmm. we are done with the inode, and in the process of freeing
+ * the state_owner. we keep this around to process errors
+ */
+ nfs4_increment_seqid(task->tk_status, sp);
+ switch (task->tk_status) {
+ case 0:
+ state->state = calldata->arg.open_flags;
+ memcpy(&state->stateid, &calldata->res.stateid,
+ sizeof(state->stateid));
+ break;
+ case -NFS4ERR_STALE_STATEID:
+ case -NFS4ERR_EXPIRED:
+ state->state = calldata->arg.open_flags;
+ nfs4_schedule_state_recovery(server->nfs4_state);
+ break;
+ default:
+ if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+ rpc_restart_call(task);
+ return;
+ }
+ }
+ nfs4_put_open_state(state);
+ up(&sp->so_sema);
+ nfs4_put_state_owner(sp);
+ up_read(&server->nfs4_state->cl_sem);
+ kfree(calldata);
+}
+
+static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *calldata)
+{
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
+ .rpc_argp = &calldata->arg,
+ .rpc_resp = &calldata->res,
+ .rpc_cred = calldata->state->owner->so_cred,
+ };
+ if (calldata->arg.open_flags != 0)
+ msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
+ return rpc_call_async(clnt, &msg, 0, nfs4_close_done, calldata);
+}
+
/*
* It is possible for data to be read/written from a mem-mapped file
* after the sys_close call (which hits the vfs layer as a flush).
@@ -669,102 +726,34 @@ int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/
-static int _nfs4_do_close(struct inode *inode, struct nfs4_state *state)
+int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode)
{
- struct nfs4_state_owner *sp = state->owner;
- int status = 0;
- struct nfs_closeargs arg = {
- .fh = NFS_FH(inode),
- };
- struct nfs_closeres res;
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
- .rpc_argp = &arg,
- .rpc_resp = &res,
- };
+ struct nfs4_closedata *calldata;
+ int status;
- if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+ /* Tell caller we're done */
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+ state->state = mode;
return 0;
- memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
+ }
+ calldata = (struct nfs4_closedata *)kmalloc(sizeof(*calldata), GFP_KERNEL);
+ if (calldata == NULL)
+ return -ENOMEM;
+ calldata->inode = inode;
+ calldata->state = state;
+ calldata->arg.fh = NFS_FH(inode);
/* Serialization for the sequence id */
- arg.seqid = sp->so_seqid,
- status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR);
-
- /* hmm. we are done with the inode, and in the process of freeing
- * the state_owner. we keep this around to process errors
+ calldata->arg.seqid = state->owner->so_seqid;
+ calldata->arg.open_flags = mode;
+ memcpy(&calldata->arg.stateid, &state->stateid,
+ sizeof(calldata->arg.stateid));
+ status = nfs4_close_call(NFS_SERVER(inode)->client, calldata);
+ /*
+ * Return -EINPROGRESS on success in order to indicate to the
+ * caller that an asynchronous RPC call has been launched, and
+ * that it will release the semaphores on completion.
*/
- nfs4_increment_seqid(status, sp);
- if (!status)
- memcpy(&state->stateid, &res.stateid, sizeof(state->stateid));
-
- return status;
-}
-
-int nfs4_do_close(struct inode *inode, struct nfs4_state *state)
-{
- struct nfs_server *server = NFS_SERVER(state->inode);
- struct nfs4_exception exception = { };
- int err;
- do {
- err = _nfs4_do_close(inode, state);
- switch (err) {
- case -NFS4ERR_STALE_STATEID:
- case -NFS4ERR_EXPIRED:
- nfs4_schedule_state_recovery(server->nfs4_state);
- err = 0;
- default:
- state->state = 0;
- }
- err = nfs4_handle_exception(server, err, &exception);
- } while (exception.retry);
- return err;
-}
-
-static int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
-{
- struct nfs4_state_owner *sp = state->owner;
- int status = 0;
- struct nfs_closeargs arg = {
- .fh = NFS_FH(inode),
- .seqid = sp->so_seqid,
- .open_flags = mode,
- };
- struct nfs_closeres res;
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE],
- .rpc_argp = &arg,
- .rpc_resp = &res,
- };
-
- if (test_bit(NFS_DELEGATED_STATE, &state->flags))
- return 0;
- memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
- status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR);
- nfs4_increment_seqid(status, sp);
- if (!status)
- memcpy(&state->stateid, &res.stateid, sizeof(state->stateid));
-
- return status;
-}
-
-int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
-{
- struct nfs_server *server = NFS_SERVER(state->inode);
- struct nfs4_exception exception = { };
- int err;
- do {
- err = _nfs4_do_downgrade(inode, state, mode);
- switch (err) {
- case -NFS4ERR_STALE_STATEID:
- case -NFS4ERR_EXPIRED:
- nfs4_schedule_state_recovery(server->nfs4_state);
- err = 0;
- default:
- state->state = mode;
- }
- err = nfs4_handle_exception(server, err, &exception);
- } while (exception.retry);
- return err;
+ return (status == 0) ? -EINPROGRESS : status;
}
struct inode *
@@ -785,7 +774,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
}
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
- state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred);
+ state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
put_rpccred(cred);
if (IS_ERR(state))
return (struct inode *)state;
@@ -802,7 +791,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
if (IS_ERR(state))
- state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred);
+ state = nfs4_do_open(dir, dentry, openflags, NULL, cred);
put_rpccred(cred);
if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0)
return 1;
@@ -1026,7 +1015,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
FMODE_WRITE, cred);
if (IS_ERR(state))
state = nfs4_do_open(dentry->d_parent->d_inode,
- &dentry->d_name, FMODE_WRITE,
+ dentry, FMODE_WRITE,
NULL, cred);
need_iput = 1;
}
@@ -1327,7 +1316,7 @@ static int nfs4_proc_commit(struct nfs_write_data *cdata)
*/
static struct inode *
-nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
+nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
{
struct inode *inode;
@@ -1335,7 +1324,7 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
struct rpc_cred *cred;
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
- state = nfs4_do_open(dir, name, flags, sattr, cred);
+ state = nfs4_do_open(dir, dentry, flags, sattr, cred);
put_rpccred(cred);
if (!IS_ERR(state)) {
inode = state->inode;
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 91e9bd77e0e990..4136e49a2d9154 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -445,7 +445,7 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
state->owner = owner;
atomic_inc(&owner->so_count);
list_add(&state->inode_states, &nfsi->open_states);
- state->inode = inode;
+ state->inode = igrab(inode);
spin_unlock(&inode->i_lock);
} else {
spin_unlock(&inode->i_lock);
@@ -471,6 +471,7 @@ void nfs4_put_open_state(struct nfs4_state *state)
list_del(&state->inode_states);
spin_unlock(&inode->i_lock);
list_del(&state->open_states);
+ iput(inode);
BUG_ON (state->state != 0);
nfs4_free_open_state(state);
nfs4_put_state_owner(owner);
@@ -486,7 +487,6 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
struct nfs4_state_owner *owner = state->owner;
struct nfs4_client *clp = owner->so_client;
int newstate;
- int status = 0;
atomic_inc(&owner->so_count);
down_read(&clp->cl_sem);
@@ -508,10 +508,8 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
newstate |= FMODE_WRITE;
if (state->state == newstate)
goto out;
- if (newstate != 0)
- status = nfs4_do_downgrade(inode, state, newstate);
- else
- status = nfs4_do_close(inode, state);
+ if (nfs4_do_close(inode, state, newstate) == -EINPROGRESS)
+ return;
}
out:
nfs4_put_open_state(state);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index ae9500199b2d09..3ddaaa1e937424 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -63,12 +63,12 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
dprintk("%s: call getattr\n", __FUNCTION__);
fattr->valid = 0;
status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0);
- dprintk("%s: reply getattr %d\n", __FUNCTION__, status);
+ dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
if (status)
return status;
dprintk("%s: call statfs\n", __FUNCTION__);
status = rpc_call(server->client_sys, NFSPROC_STATFS, fhandle, &fsinfo, 0);
- dprintk("%s: reply statfs %d\n", __FUNCTION__, status);
+ dprintk("%s: reply statfs: %d\n", __FUNCTION__, status);
if (status)
return status;
info->rtmax = NFS_MAXDATA;
@@ -96,7 +96,7 @@ nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_GETATTR,
fhandle, fattr, 0);
- dprintk("NFS reply getattr\n");
+ dprintk("NFS reply getattr: %d\n", status);
return status;
}
@@ -114,7 +114,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
dprintk("NFS call setattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0);
- dprintk("NFS reply setattr\n");
+ dprintk("NFS reply setattr: %d\n", status);
return status;
}
@@ -213,15 +213,15 @@ static int nfs_proc_write(struct nfs_write_data *wdata)
}
static struct inode *
-nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
+nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
struct nfs_createargs arg = {
.fh = NFS_FH(dir),
- .name = name->name,
- .len = name->len,
+ .name = dentry->d_name.name,
+ .len = dentry->d_name.len,
.sattr = sattr
};
struct nfs_diropok res = {
@@ -231,7 +231,7 @@ nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
int status;
fattr.valid = 0;
- dprintk("NFS call create %s\n", name->name);
+ dprintk("NFS call create %s\n", dentry->d_name.name);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
dprintk("NFS reply create: %d\n", status);
if (status == 0) {
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 0de2e2d120154a..cb6f7bfe373b23 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -24,7 +24,6 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
-#include <linux/mempool.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
@@ -39,25 +38,11 @@ static void nfs_readpage_result_partial(struct nfs_read_data *, int);
static void nfs_readpage_result_full(struct nfs_read_data *, int);
static kmem_cache_t *nfs_rdata_cachep;
-static mempool_t *nfs_rdata_mempool;
+mempool_t *nfs_rdata_mempool;
#define MIN_POOL_READ (32)
-static struct nfs_read_data *nfs_readdata_alloc(void)
-{
- struct nfs_read_data *p;
- p = (struct nfs_read_data *)mempool_alloc(nfs_rdata_mempool, SLAB_NOFS);
- if (p)
- memset(p, 0, sizeof(*p));
- return p;
-}
-
-static __inline__ void nfs_readdata_free(struct nfs_read_data *p)
-{
- mempool_free(p, nfs_rdata_mempool);
-}
-
-static void nfs_readdata_release(struct rpc_task *task)
+void nfs_readdata_release(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata;
nfs_readdata_free(data);
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index f92eeea330ff6e..2b0a0cc642aa23 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -215,7 +215,6 @@ nfs_complete_unlink(struct dentry *dentry)
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
spin_unlock(&dentry->d_lock);
- if (data->task.tk_rpcwait == &nfs_delete_queue)
- rpc_wake_up_task(&data->task);
+ rpc_wake_up_task(&data->task);
nfs_put_unlinkdata(data);
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 847b77d4c7aedd..ad7eb9bdff4620 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -61,7 +61,6 @@
#include <linux/nfs_page.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
-#include <linux/mempool.h>
#include "delegation.h"
@@ -83,49 +82,17 @@ static int nfs_wait_on_write_congestion(struct address_space *, int);
static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int);
static kmem_cache_t *nfs_wdata_cachep;
-static mempool_t *nfs_wdata_mempool;
-static mempool_t *nfs_commit_mempool;
+mempool_t *nfs_wdata_mempool;
+mempool_t *nfs_commit_mempool;
static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion);
-static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
-{
- struct nfs_write_data *p;
- p = (struct nfs_write_data *)mempool_alloc(nfs_wdata_mempool, SLAB_NOFS);
- if (p) {
- memset(p, 0, sizeof(*p));
- INIT_LIST_HEAD(&p->pages);
- }
- return p;
-}
-
-static __inline__ void nfs_writedata_free(struct nfs_write_data *p)
-{
- mempool_free(p, nfs_wdata_mempool);
-}
-
-static void nfs_writedata_release(struct rpc_task *task)
+void nfs_writedata_release(struct rpc_task *task)
{
struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata;
nfs_writedata_free(wdata);
}
-static __inline__ struct nfs_write_data *nfs_commit_alloc(void)
-{
- struct nfs_write_data *p;
- p = (struct nfs_write_data *)mempool_alloc(nfs_commit_mempool, SLAB_NOFS);
- if (p) {
- memset(p, 0, sizeof(*p));
- INIT_LIST_HEAD(&p->pages);
- }
- return p;
-}
-
-static __inline__ void nfs_commit_free(struct nfs_write_data *p)
-{
- mempool_free(p, nfs_commit_mempool);
-}
-
/* Adjust the file length if we're writing beyond the end */
static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
{
@@ -184,11 +151,10 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
int result, written = 0;
struct nfs_write_data *wdata;
- wdata = kmalloc(sizeof(*wdata), GFP_NOFS);
+ wdata = nfs_writedata_alloc();
if (!wdata)
return -ENOMEM;
- memset(wdata, 0, sizeof(*wdata));
wdata->flags = how;
wdata->cred = ctx->cred;
wdata->inode = inode;
@@ -238,8 +204,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
io_error:
nfs_end_data_update_defer(inode);
-
- kfree(wdata);
+ nfs_writedata_free(wdata);
return written ? written : result;
}
@@ -1199,7 +1164,8 @@ void nfs_writeback_done(struct rpc_task *task)
}
if (time_before(complain, jiffies)) {
printk(KERN_WARNING
- "NFS: Server wrote less than requested.\n");
+ "NFS: Server wrote zero bytes, expected %u.\n",
+ argp->count);
complain = jiffies + 300 * HZ;
}
/* Can't do anything about it except throw an error. */