diff options
author | Trond Myklebust <trond.myklebust@fys.uio.no> | 2005-01-05 02:17:13 +0100 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@fys.uio.no> | 2005-01-05 02:17:13 +0100 |
commit | ca486f406c8c180c964dbe52a4d36429f4865d40 (patch) | |
tree | 0b7461d72e49a4b722e4cae21609adf151d0b523 /fs | |
parent | e83235934b015fa6cd32736bb898cfb58c6d7a1a (diff) | |
parent | 9e84df776f2ce9cd66e51cc55afc821468597556 (diff) | |
download | history-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.c | 48 | ||||
-rw-r--r-- | fs/locks.c | 6 | ||||
-rw-r--r-- | fs/nfs/dir.c | 225 | ||||
-rw-r--r-- | fs/nfs/direct.c | 425 | ||||
-rw-r--r-- | fs/nfs/file.c | 50 | ||||
-rw-r--r-- | fs/nfs/inode.c | 107 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 20 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 189 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 10 | ||||
-rw-r--r-- | fs/nfs/proc.c | 16 | ||||
-rw-r--r-- | fs/nfs/read.c | 19 | ||||
-rw-r--r-- | fs/nfs/unlink.c | 3 | ||||
-rw-r--r-- | fs/nfs/write.c | 48 |
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. */ |