2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* linux/fs/nfs/inode.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992 Rick Sladkey
|
|
|
|
*
|
|
|
|
* nfs inode and superblock handling functions
|
|
|
|
*
|
2008-10-27 18:19:48 +03:00
|
|
|
* Modularised by Alan Cox <alan@lxorguk.ukuu.org.uk>, while hacking some
|
2005-04-17 02:20:36 +04:00
|
|
|
* experimental NFS changes. Modularisation taken straight from SYS5 fs.
|
|
|
|
*
|
|
|
|
* Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
|
|
|
|
* J.S.Peatfield@damtp.cam.ac.uk
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
Detach sched.h from mm.h
First thing mm.h does is including sched.h solely for can_do_mlock() inline
function which has "current" dereference inside. By dealing with can_do_mlock()
mm.h can be detached from sched.h which is good. See below, why.
This patch
a) removes unconditional inclusion of sched.h from mm.h
b) makes can_do_mlock() normal function in mm/mlock.c
c) exports can_do_mlock() to not break compilation
d) adds sched.h inclusions back to files that were getting it indirectly.
e) adds less bloated headers to some files (asm/signal.h, jiffies.h) that were
getting them indirectly
Net result is:
a) mm.h users would get less code to open, read, preprocess, parse, ... if
they don't need sched.h
b) sched.h stops being dependency for significant number of files:
on x86_64 allmodconfig touching sched.h results in recompile of 4083 files,
after patch it's only 3744 (-8.3%).
Cross-compile tested on
all arm defconfigs, all mips defconfigs, all powerpc defconfigs,
alpha alpha-up
arm
i386 i386-up i386-defconfig i386-allnoconfig
ia64 ia64-up
m68k
mips
parisc parisc-up
powerpc powerpc-up
s390 s390-up
sparc sparc-up
sparc64 sparc64-up
um-x86_64
x86_64 x86_64-up x86_64-defconfig x86_64-allnoconfig
as well as my two usual configs.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-21 01:22:52 +04:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/stat.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/unistd.h>
|
|
|
|
#include <linux/sunrpc/clnt.h>
|
|
|
|
#include <linux/sunrpc/stats.h>
|
2006-03-20 21:44:22 +03:00
|
|
|
#include <linux/sunrpc/metrics.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/nfs_fs.h>
|
|
|
|
#include <linux/nfs_mount.h>
|
|
|
|
#include <linux/nfs4_mount.h>
|
|
|
|
#include <linux/lockd/bind.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/vfs.h>
|
2006-06-09 17:34:28 +04:00
|
|
|
#include <linux/inet.h>
|
|
|
|
#include <linux/nfs_xdr.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 11:04:11 +03:00
|
|
|
#include <linux/slab.h>
|
2010-12-02 22:31:23 +03:00
|
|
|
#include <linux/compat.h>
|
2011-12-02 01:44:39 +04:00
|
|
|
#include <linux/freezer.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
2005-06-22 21:16:21 +04:00
|
|
|
#include "nfs4_fs.h"
|
2006-01-03 11:55:41 +03:00
|
|
|
#include "callback.h"
|
2005-04-17 02:20:36 +04:00
|
|
|
#include "delegation.h"
|
2006-03-20 21:44:13 +03:00
|
|
|
#include "iostat.h"
|
NFS: Split fs/nfs/inode.c
As fs/nfs/inode.c is rather large, heterogenous and unwieldy, the attached
patch splits it up into a number of files:
(*) fs/nfs/inode.c
Strictly inode specific functions.
(*) fs/nfs/super.c
Superblock management functions for NFS and NFS4, normal access, clones
and referrals. The NFS4 superblock functions _could_ move out into a
separate conditionally compiled file, but it's probably not worth it as
there're so many common bits.
(*) fs/nfs/namespace.c
Some namespace-specific functions have been moved here.
(*) fs/nfs/nfs4namespace.c
NFS4-specific namespace functions (this could be merged into the previous
file). This file is conditionally compiled.
(*) fs/nfs/internal.h
Inter-file declarations, plus a few simple utility functions moved from
fs/nfs/inode.c.
Additionally, all the in-.c-file externs have been moved here, and those
files they were moved from now includes this file.
For the most part, the functions have not been changed, only some multiplexor
functions have changed significantly.
I've also:
(*) Added some extra banner comments above some functions.
(*) Rearranged the function order within the files to be more logical and
better grouped (IMO), though someone may prefer a different order.
(*) Reduced the number of #ifdefs in .c files.
(*) Added missing __init and __exit directives.
Signed-Off-By: David Howells <dhowells@redhat.com>
2006-06-09 17:34:33 +04:00
|
|
|
#include "internal.h"
|
2009-04-03 19:42:42 +04:00
|
|
|
#include "fscache.h"
|
2010-10-20 08:18:01 +04:00
|
|
|
#include "pnfs.h"
|
2012-07-31 00:05:16 +04:00
|
|
|
#include "nfs.h"
|
2011-11-25 18:13:04 +04:00
|
|
|
#include "netns.h"
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-08-20 02:59:33 +04:00
|
|
|
#include "nfstrace.h"
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
#define NFSDBG_FACILITY NFSDBG_VFS
|
|
|
|
|
2007-10-09 20:01:04 +04:00
|
|
|
#define NFS_64_BIT_INODE_NUMBERS_ENABLED 1
|
|
|
|
|
|
|
|
/* Default is to see 64-bit inode numbers */
|
2012-01-13 03:02:20 +04:00
|
|
|
static bool enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
|
2007-10-09 20:01:04 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static void nfs_invalidate_inode(struct inode *);
|
2005-12-03 23:20:07 +03:00
|
|
|
static int nfs_update_inode(struct inode *, struct nfs_fattr *);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-12-07 07:33:20 +03:00
|
|
|
static struct kmem_cache * nfs_inode_cachep;
|
2005-06-22 21:16:27 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static inline unsigned long
|
|
|
|
nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
return nfs_fileid_to_ino_t(fattr->fileid);
|
|
|
|
}
|
|
|
|
|
2009-03-11 21:10:30 +03:00
|
|
|
/**
|
|
|
|
* nfs_wait_bit_killable - helper for functions that are sleeping on bit locks
|
|
|
|
* @word: long word containing the bit lock
|
|
|
|
*/
|
2014-07-07 09:16:04 +04:00
|
|
|
int nfs_wait_bit_killable(struct wait_bit_key *key)
|
2009-03-11 21:10:30 +03:00
|
|
|
{
|
|
|
|
if (fatal_signal_pending(current))
|
|
|
|
return -ERESTARTSYS;
|
2013-05-07 03:50:06 +04:00
|
|
|
freezable_schedule_unsafe();
|
2009-03-11 21:10:30 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_wait_bit_killable);
|
2009-03-11 21:10:30 +03:00
|
|
|
|
2007-10-09 20:01:04 +04:00
|
|
|
/**
|
|
|
|
* nfs_compat_user_ino64 - returns the user-visible inode number
|
|
|
|
* @fileid: 64-bit fileid
|
|
|
|
*
|
|
|
|
* This function returns a 32-bit inode number if the boot parameter
|
|
|
|
* nfs.enable_ino64 is zero.
|
|
|
|
*/
|
|
|
|
u64 nfs_compat_user_ino64(u64 fileid)
|
|
|
|
{
|
2010-12-02 22:31:23 +03:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
compat_ulong_t ino;
|
|
|
|
#else
|
|
|
|
unsigned long ino;
|
|
|
|
#endif
|
2007-10-09 20:01:04 +04:00
|
|
|
|
|
|
|
if (enable_ino64)
|
|
|
|
return fileid;
|
|
|
|
ino = fileid;
|
|
|
|
if (sizeof(ino) < sizeof(fileid))
|
|
|
|
ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8;
|
|
|
|
return ino;
|
|
|
|
}
|
|
|
|
|
2012-12-14 23:36:36 +04:00
|
|
|
int nfs_drop_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
return NFS_STALE(inode) || generic_drop_inode(inode);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_drop_inode);
|
|
|
|
|
2012-07-31 00:05:21 +04:00
|
|
|
void nfs_clear_inode(struct inode *inode)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-06-02 01:26:35 +04:00
|
|
|
/*
|
|
|
|
* The following should never happen...
|
|
|
|
*/
|
2012-10-16 00:19:30 +04:00
|
|
|
WARN_ON_ONCE(nfs_have_writebacks(inode));
|
|
|
|
WARN_ON_ONCE(!list_empty(&NFS_I(inode)->open_files));
|
2005-06-22 21:16:22 +04:00
|
|
|
nfs_zap_acl_cache(inode);
|
2006-07-25 19:28:18 +04:00
|
|
|
nfs_access_zap_cache(inode);
|
2013-09-27 14:20:03 +04:00
|
|
|
nfs_fscache_clear_inode(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_clear_inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2010-06-07 22:34:48 +04:00
|
|
|
void nfs_evict_inode(struct inode *inode)
|
|
|
|
{
|
2014-04-04 01:47:49 +04:00
|
|
|
truncate_inode_pages_final(&inode->i_data);
|
2012-05-03 16:48:02 +04:00
|
|
|
clear_inode(inode);
|
2010-06-07 22:34:48 +04:00
|
|
|
nfs_clear_inode(inode);
|
|
|
|
}
|
|
|
|
|
2015-03-26 00:23:31 +03:00
|
|
|
int nfs_sync_inode(struct inode *inode)
|
2015-03-25 23:38:33 +03:00
|
|
|
{
|
|
|
|
nfs_inode_dio_wait(inode);
|
|
|
|
return nfs_wb_all(inode);
|
|
|
|
}
|
2015-03-26 00:23:31 +03:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_sync_inode);
|
2015-03-25 23:38:33 +03:00
|
|
|
|
2005-12-14 00:13:54 +03:00
|
|
|
/**
|
|
|
|
* nfs_sync_mapping - helper to flush all mmapped dirty data to disk
|
|
|
|
*/
|
|
|
|
int nfs_sync_mapping(struct address_space *mapping)
|
|
|
|
{
|
2010-02-20 04:03:29 +03:00
|
|
|
int ret = 0;
|
2005-12-14 00:13:54 +03:00
|
|
|
|
2010-02-20 04:03:29 +03:00
|
|
|
if (mapping->nrpages != 0) {
|
|
|
|
unmap_mapping_range(mapping, 0, 0, 0);
|
|
|
|
ret = nfs_wb_all(mapping->host);
|
|
|
|
}
|
2005-12-14 00:13:54 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-20 21:11:01 +04:00
|
|
|
static void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
|
|
|
if (inode->i_mapping->nrpages == 0)
|
|
|
|
flags &= ~NFS_INO_INVALID_DATA;
|
|
|
|
nfsi->cache_validity |= flags;
|
|
|
|
if (flags & NFS_INO_INVALID_DATA)
|
|
|
|
nfs_fscache_invalidate(inode);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Invalidate the local caches
|
|
|
|
*/
|
2005-11-26 01:10:06 +03:00
|
|
|
static void nfs_zap_caches_locked(struct inode *inode)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
int mode = inode->i_mode;
|
|
|
|
|
2006-03-20 21:44:14 +03:00
|
|
|
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
|
|
|
|
|
2007-09-29 03:22:40 +04:00
|
|
|
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
|
|
|
|
nfsi->attrtimeo_timestamp = jiffies;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-09-03 22:56:02 +04:00
|
|
|
memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
|
2012-12-21 01:52:38 +04:00
|
|
|
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
|
2013-05-22 20:50:44 +04:00
|
|
|
| NFS_INO_INVALID_DATA
|
|
|
|
| NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL
|
2014-06-20 21:11:01 +04:00
|
|
|
| NFS_INO_REVAL_PAGECACHE);
|
2013-05-22 20:50:44 +04:00
|
|
|
} else
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
|
2013-05-22 20:50:44 +04:00
|
|
|
| NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL
|
2014-06-20 21:11:01 +04:00
|
|
|
| NFS_INO_REVAL_PAGECACHE);
|
2014-02-06 23:38:53 +04:00
|
|
|
nfs_zap_label_cache_locked(nfsi);
|
2005-11-26 01:10:06 +03:00
|
|
|
}
|
2005-08-18 22:24:12 +04:00
|
|
|
|
2005-11-26 01:10:06 +03:00
|
|
|
void nfs_zap_caches(struct inode *inode)
|
|
|
|
{
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
nfs_zap_caches_locked(inode);
|
2005-08-18 22:24:12 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
2005-06-22 21:16:22 +04:00
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:40 +04:00
|
|
|
void nfs_zap_mapping(struct inode *inode, struct address_space *mapping)
|
|
|
|
{
|
|
|
|
if (mapping->nrpages != 0) {
|
|
|
|
spin_lock(&inode->i_lock);
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
|
2006-10-20 10:28:40 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-12 01:39:04 +04:00
|
|
|
void nfs_zap_acl_cache(struct inode *inode)
|
2005-06-22 21:16:22 +04:00
|
|
|
{
|
|
|
|
void (*clear_acl_cache)(struct inode *);
|
|
|
|
|
|
|
|
clear_acl_cache = NFS_PROTO(inode)->clear_acl_cache;
|
|
|
|
if (clear_acl_cache != NULL)
|
|
|
|
clear_acl_cache(inode);
|
2005-08-18 22:24:12 +04:00
|
|
|
spin_lock(&inode->i_lock);
|
2005-08-18 22:24:09 +04:00
|
|
|
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ACL;
|
2005-08-18 22:24:12 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2012-07-31 00:05:24 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_zap_acl_cache);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-09-29 01:11:45 +04:00
|
|
|
void nfs_invalidate_atime(struct inode *inode)
|
|
|
|
{
|
|
|
|
spin_lock(&inode->i_lock);
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
|
2007-09-29 01:11:45 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_invalidate_atime);
|
2007-09-29 01:11:45 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
2005-11-26 01:10:06 +03:00
|
|
|
* Invalidate, but do not unhash, the inode.
|
|
|
|
* NB: must be called with inode->i_lock held!
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2005-11-26 01:10:06 +03:00
|
|
|
static void nfs_invalidate_inode(struct inode *inode)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-01-23 09:58:59 +03:00
|
|
|
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
|
2005-11-26 01:10:06 +03:00
|
|
|
nfs_zap_caches_locked(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct nfs_find_desc {
|
|
|
|
struct nfs_fh *fh;
|
|
|
|
struct nfs_fattr *fattr;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In NFSv3 we can have 64bit inode numbers. In order to support
|
|
|
|
* this, and re-exported directories (also seen in NFSv2)
|
|
|
|
* we are forced to allow 2 different inodes to have the same
|
|
|
|
* i_ino.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
nfs_find_actor(struct inode *inode, void *opaque)
|
|
|
|
{
|
|
|
|
struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
|
|
|
|
struct nfs_fh *fh = desc->fh;
|
|
|
|
struct nfs_fattr *fattr = desc->fattr;
|
|
|
|
|
|
|
|
if (NFS_FILEID(inode) != fattr->fileid)
|
|
|
|
return 0;
|
nfs: don't allow nfs_find_actor to match inodes of the wrong type
Benny Halevy reported the following oops when testing RHEL6:
<7>nfs_update_inode: inode 892950 mode changed, 0040755 to 0100644
<1>BUG: unable to handle kernel NULL pointer dereference at (null)
<1>IP: [<ffffffffa02a52c5>] nfs_closedir+0x15/0x30 [nfs]
<4>PGD 81448a067 PUD 831632067 PMD 0
<4>Oops: 0000 [#1] SMP
<4>last sysfs file: /sys/kernel/mm/redhat_transparent_hugepage/enabled
<4>CPU 6
<4>Modules linked in: fuse bonding 8021q garp ebtable_nat ebtables be2iscsi iscsi_boot_sysfs bnx2i cnic uio cxgb4i cxgb4 cxgb3i libcxgbi cxgb3 mdio ib_iser rdma_cm ib_cm iw_cm ib_sa ib_mad ib_core ib_addr iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi softdog bridge stp llc xt_physdev ipt_REJECT nf_conntrack_ipv4 nf_defrag_ipv4 xt_multiport iptable_filter ip_tables ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables ipv6 dm_round_robin dm_multipath objlayoutdriver2(U) nfs(U) lockd fscache auth_rpcgss nfs_acl sunrpc vhost_net macvtap macvlan tun kvm_intel kvm be2net igb dca ptp pps_core microcode serio_raw sg iTCO_wdt iTCO_vendor_support i7core_edac edac_core shpchp ext4 mbcache jbd2 sd_mod crc_t10dif ahci dm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]
<4>
<4>Pid: 6332, comm: dd Not tainted 2.6.32-358.el6.x86_64 #1 HP ProLiant DL170e G6 /ProLiant DL170e G6
<4>RIP: 0010:[<ffffffffa02a52c5>] [<ffffffffa02a52c5>] nfs_closedir+0x15/0x30 [nfs]
<4>RSP: 0018:ffff88081458bb98 EFLAGS: 00010292
<4>RAX: ffffffffa02a52b0 RBX: 0000000000000000 RCX: 0000000000000003
<4>RDX: ffffffffa02e45a0 RSI: ffff88081440b300 RDI: ffff88082d5f5760
<4>RBP: ffff88081458bba8 R08: 0000000000000000 R09: 0000000000000000
<4>R10: 0000000000000772 R11: 0000000000400004 R12: 0000000040000008
<4>R13: ffff88082d5f5760 R14: ffff88082d6e8800 R15: ffff88082f12d780
<4>FS: 00007f728f37e700(0000) GS:ffff8800456c0000(0000) knlGS:0000000000000000
<4>CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
<4>CR2: 0000000000000000 CR3: 0000000831279000 CR4: 00000000000007e0
<4>DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
<4>DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
<4>Process dd (pid: 6332, threadinfo ffff88081458a000, task ffff88082fa0e040)
<4>Stack:
<4> 0000000040000008 ffff88081440b300 ffff88081458bbf8 ffffffff81182745
<4><d> ffff88082d5f5760 ffff88082d6e8800 ffff88081458bbf8 ffffffffffffffea
<4><d> ffff88082f12d780 ffff88082d6e8800 ffffffffa02a50a0 ffff88082d5f5760
<4>Call Trace:
<4> [<ffffffff81182745>] __fput+0xf5/0x210
<4> [<ffffffffa02a50a0>] ? do_open+0x0/0x20 [nfs]
<4> [<ffffffff81182885>] fput+0x25/0x30
<4> [<ffffffff8117e23e>] __dentry_open+0x27e/0x360
<4> [<ffffffff811c397a>] ? inotify_d_instantiate+0x2a/0x60
<4> [<ffffffff8117e4b9>] lookup_instantiate_filp+0x69/0x90
<4> [<ffffffffa02a6679>] nfs_intent_set_file+0x59/0x90 [nfs]
<4> [<ffffffffa02a686b>] nfs_atomic_lookup+0x1bb/0x310 [nfs]
<4> [<ffffffff8118e0c2>] __lookup_hash+0x102/0x160
<4> [<ffffffff81225052>] ? selinux_inode_permission+0x72/0xb0
<4> [<ffffffff8118e76a>] lookup_hash+0x3a/0x50
<4> [<ffffffff81192a4b>] do_filp_open+0x2eb/0xdd0
<4> [<ffffffff8104757c>] ? __do_page_fault+0x1ec/0x480
<4> [<ffffffff8119f562>] ? alloc_fd+0x92/0x160
<4> [<ffffffff8117de79>] do_sys_open+0x69/0x140
<4> [<ffffffff811811f6>] ? sys_lseek+0x66/0x80
<4> [<ffffffff8117df90>] sys_open+0x20/0x30
<4> [<ffffffff8100b072>] system_call_fastpath+0x16/0x1b
<4>Code: 65 48 8b 04 25 c8 cb 00 00 83 a8 44 e0 ff ff 01 5b 41 5c c9 c3 90 55 48 89 e5 53 48 83 ec 08 0f 1f 44 00 00 48 8b 9e a0 00 00 00 <48> 8b 3b e8 13 0c f7 ff 48 89 df e8 ab 3d ec e0 48 83 c4 08 31
<1>RIP [<ffffffffa02a52c5>] nfs_closedir+0x15/0x30 [nfs]
<4> RSP <ffff88081458bb98>
<4>CR2: 0000000000000000
I think this is ultimately due to a bug on the server. The client had
previously found a directory dentry. It then later tried to do an atomic
open on a new (regular file) dentry. The attributes it got back had the
same filehandle as the previously found directory inode. It then tried
to put the filp because it failed the aops tests for O_DIRECT opens, and
oopsed here because the ctx was still NULL.
Obviously the root cause here is a server issue, but we can take steps
to mitigate this on the client. When nfs_fhget is called, we always know
what type of inode it is. In the event that there's a broken or
malicious server on the other end of the wire, the client can end up
crashing because the wrong ops are set on it.
Have nfs_find_actor check that the inode type is correct after checking
the fileid. The fileid check should rarely ever match, so it should only
rarely ever get to this check. In the case where we have a broken
server, we may see two different inodes with the same i_ino, but the
client should be able to cope with them without crashing.
This should fix the oops reported here:
https://bugzilla.redhat.com/show_bug.cgi?id=913660
Reported-by: Benny Halevy <bhalevy@tonian.com>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-02-28 05:10:34 +04:00
|
|
|
if ((S_IFMT & inode->i_mode) != (S_IFMT & fattr->mode))
|
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
if (nfs_compare_fh(NFS_FH(inode), fh))
|
|
|
|
return 0;
|
|
|
|
if (is_bad_inode(inode) || NFS_STALE(inode))
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nfs_init_locked(struct inode *inode, void *opaque)
|
|
|
|
{
|
|
|
|
struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
|
|
|
|
struct nfs_fattr *fattr = desc->fattr;
|
|
|
|
|
2008-01-23 09:59:08 +03:00
|
|
|
set_nfs_fileid(inode, fattr->fileid);
|
2005-04-17 02:20:36 +04:00
|
|
|
nfs_copy_fh(NFS_FH(inode), desc->fh);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-22 20:50:40 +04:00
|
|
|
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
|
2014-02-06 23:38:53 +04:00
|
|
|
static void nfs_clear_label_invalid(struct inode *inode)
|
|
|
|
{
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_LABEL;
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
|
|
|
|
2013-05-22 20:50:44 +04:00
|
|
|
void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
|
|
|
|
struct nfs4_label *label)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (label == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
|
|
|
|
error = security_inode_notifysecctx(inode, label->label,
|
|
|
|
label->len);
|
|
|
|
if (error)
|
|
|
|
printk(KERN_ERR "%s() %s %d "
|
|
|
|
"security_inode_notifysecctx() %d\n",
|
|
|
|
__func__,
|
|
|
|
(char *)label->label,
|
|
|
|
label->len, error);
|
2014-02-06 23:38:53 +04:00
|
|
|
nfs_clear_label_invalid(inode);
|
2013-05-22 20:50:44 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-22 20:50:40 +04:00
|
|
|
struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags)
|
|
|
|
{
|
|
|
|
struct nfs4_label *label = NULL;
|
|
|
|
int minor_version = server->nfs_client->cl_minorversion;
|
|
|
|
|
|
|
|
if (minor_version < 2)
|
|
|
|
return label;
|
|
|
|
|
|
|
|
if (!(server->caps & NFS_CAP_SECURITY_LABEL))
|
|
|
|
return label;
|
|
|
|
|
|
|
|
label = kzalloc(sizeof(struct nfs4_label), flags);
|
|
|
|
if (label == NULL)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
label->label = kzalloc(NFS4_MAXLABELLEN, flags);
|
|
|
|
if (label->label == NULL) {
|
|
|
|
kfree(label);
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
label->len = NFS4_MAXLABELLEN;
|
|
|
|
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs4_label_alloc);
|
2013-05-22 20:50:44 +04:00
|
|
|
#else
|
2013-11-18 23:33:50 +04:00
|
|
|
void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
|
2013-05-22 20:50:44 +04:00
|
|
|
struct nfs4_label *label)
|
|
|
|
{
|
|
|
|
}
|
2013-05-22 20:50:40 +04:00
|
|
|
#endif
|
2013-05-22 20:50:44 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_setsecurity);
|
2013-05-22 20:50:40 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* This is our front-end to iget that looks up inodes by file handle
|
|
|
|
* instead of inode number.
|
|
|
|
*/
|
|
|
|
struct inode *
|
2013-05-22 20:50:42 +04:00
|
|
|
nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, struct nfs4_label *label)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_find_desc desc = {
|
|
|
|
.fh = fh,
|
|
|
|
.fattr = fattr
|
|
|
|
};
|
2006-03-20 21:44:48 +03:00
|
|
|
struct inode *inode = ERR_PTR(-ENOENT);
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned long hash;
|
|
|
|
|
2011-03-24 20:12:30 +03:00
|
|
|
nfs_attr_check_mountpoint(sb, fattr);
|
|
|
|
|
NFS: Fix use of nfs_attr_use_mounted_on_fileid()
This function call was being optimized out during nfs_fhget(), leading
to situations where we have a valid fileid but still want to use the
mounted_on_fileid. For example, imagine we have our server configured
like this:
server % df
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 9.1G 6.5G 1.9G 78% /
/dev/vdb1 487M 2.3M 456M 1% /exports
/dev/vdc1 487M 2.3M 456M 1% /exports/vol1
/dev/vdd1 487M 2.3M 456M 1% /exports/vol2
If our client mounts /exports and tries to do a "chown -R" across the
entire mountpoint, we will get a nasty message warning us about a circular
directory structure. Running chown with strace tells me that each directory
has the same device and inode number:
newfstatat(AT_FDCWD, "/nfs/", {st_dev=makedev(0, 38), st_ino=2, ...}) = 0
newfstatat(4, "vol1", {st_dev=makedev(0, 38), st_ino=2, ...}) = 0
newfstatat(4, "vol2", {st_dev=makedev(0, 38), st_ino=2, ...}) = 0
With this patch the mounted_on_fileid values are used for st_ino, so the
directory loop warning isn't reported.
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-12-10 00:19:16 +03:00
|
|
|
if (nfs_attr_use_mounted_on_fileid(fattr))
|
|
|
|
fattr->fileid = fattr->mounted_on_fileid;
|
|
|
|
else if ((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0)
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out_no_inode;
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0)
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out_no_inode;
|
|
|
|
|
|
|
|
hash = nfs_fattr_to_ino_t(fattr);
|
|
|
|
|
2006-03-20 21:44:48 +03:00
|
|
|
inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc);
|
|
|
|
if (inode == NULL) {
|
|
|
|
inode = ERR_PTR(-ENOMEM);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out_no_inode;
|
2006-03-20 21:44:48 +03:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (inode->i_state & I_NEW) {
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2007-02-06 01:44:22 +03:00
|
|
|
unsigned long now = jiffies;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* We set i_ino for the few things that still rely on it,
|
|
|
|
* such as stat(2) */
|
|
|
|
inode->i_ino = hash;
|
|
|
|
|
|
|
|
/* We can't support update_atime(), since the server will reset it */
|
|
|
|
inode->i_flags |= S_NOATIME|S_NOCMTIME;
|
|
|
|
inode->i_mode = fattr->mode;
|
2009-08-09 23:06:19 +04:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
|
|
|
|
&& nfs_server_capable(inode, NFS_CAP_MODE))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Why so? Because we want revalidate for devices/FIFOs, and
|
|
|
|
* that's precisely what we have in nfs_file_inode_operations.
|
|
|
|
*/
|
2006-08-23 04:06:12 +04:00
|
|
|
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->file_inode_ops;
|
2005-04-17 02:20:36 +04:00
|
|
|
if (S_ISREG(inode->i_mode)) {
|
nfs: when attempting to open a directory, fall back on normal lookup (try #5)
commit d953126 changed how nfs_atomic_lookup handles an -EISDIR return
from an OPEN call. Prior to that patch, that caused the client to fall
back to doing a normal lookup. When that patch went in, the code began
returning that error to userspace. The d_revalidate codepath however
never had the corresponding change, so it was still possible to end up
with a NULL ctx->state pointer after that.
That patch caused a regression. When we attempt to open a directory that
does not have a cached dentry, that open now errors out with EISDIR. If
you attempt the same open with a cached dentry, it will succeed.
Fix this by reverting the change in nfs_atomic_lookup and allowing
attempts to open directories to fall back to a normal lookup
Also, add a NFSv4-specific f_ops->open routine that just returns
-ENOTDIR. This should never be called if things are working properly,
but if it ever is, then the dprintk may help in debugging.
To facilitate this, a new file_operations field is also added to the
nfs_rpc_ops struct.
Cc: stable@kernel.org
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-11-04 21:31:21 +04:00
|
|
|
inode->i_fop = NFS_SB(sb)->nfs_client->rpc_ops->file_ops;
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_data.a_ops = &nfs_file_aops;
|
|
|
|
} else if (S_ISDIR(inode->i_mode)) {
|
2006-08-23 04:06:12 +04:00
|
|
|
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_fop = &nfs_dir_operations;
|
2010-12-01 22:17:06 +03:00
|
|
|
inode->i_data.a_ops = &nfs_dir_aops;
|
2006-06-09 17:34:19 +04:00
|
|
|
/* Deal with crossing mountpoints */
|
2011-03-24 20:12:30 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
|
|
|
|
fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
|
2006-06-09 17:34:29 +04:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
|
|
|
|
inode->i_op = &nfs_referral_inode_operations;
|
|
|
|
else
|
|
|
|
inode->i_op = &nfs_mountpoint_inode_operations;
|
2006-06-09 17:34:19 +04:00
|
|
|
inode->i_fop = NULL;
|
2011-01-14 21:45:42 +03:00
|
|
|
inode->i_flags |= S_AUTOMOUNT;
|
2006-06-09 17:34:19 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
} else if (S_ISLNK(inode->i_mode))
|
|
|
|
inode->i_op = &nfs_symlink_inode_operations;
|
|
|
|
else
|
|
|
|
init_special_inode(inode, inode->i_mode, fattr->rdev);
|
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
memset(&inode->i_atime, 0, sizeof(inode->i_atime));
|
|
|
|
memset(&inode->i_mtime, 0, sizeof(inode->i_mtime));
|
|
|
|
memset(&inode->i_ctime, 0, sizeof(inode->i_ctime));
|
2011-10-18 03:08:46 +04:00
|
|
|
inode->i_version = 0;
|
2009-03-11 21:10:24 +03:00
|
|
|
inode->i_size = 0;
|
2011-10-28 16:13:28 +04:00
|
|
|
clear_nlink(inode);
|
2013-02-02 02:26:23 +04:00
|
|
|
inode->i_uid = make_kuid(&init_user_ns, -2);
|
|
|
|
inode->i_gid = make_kgid(&init_user_ns, -2);
|
2009-03-11 21:10:24 +03:00
|
|
|
inode->i_blocks = 0;
|
|
|
|
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
|
2012-05-24 21:13:24 +04:00
|
|
|
nfsi->write_io = 0;
|
|
|
|
nfsi->read_io = 0;
|
2009-03-11 21:10:24 +03:00
|
|
|
|
2005-10-28 06:12:39 +04:00
|
|
|
nfsi->read_cache_jiffies = fattr->time_start;
|
2008-10-15 03:16:07 +04:00
|
|
|
nfsi->attr_gencount = fattr->gencount;
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
|
|
|
|
inode->i_atime = fattr->atime;
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_ATIME))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MTIME)
|
|
|
|
inode->i_mtime = fattr->mtime;
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_MTIME))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
|
|
|
inode->i_ctime = fattr->ctime;
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_CTIME))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
|
2011-10-18 03:08:46 +04:00
|
|
|
inode->i_version = fattr->change_attr;
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SIZE)
|
|
|
|
inode->i_size = nfs_size_to_loff_t(fattr->size);
|
2009-08-09 23:06:19 +04:00
|
|
|
else
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
|
|
|
|
| NFS_INO_REVAL_PAGECACHE);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_NLINK)
|
2011-10-28 16:13:29 +04:00
|
|
|
set_nlink(inode, fattr->nlink);
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_NLINK))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_OWNER)
|
|
|
|
inode->i_uid = fattr->uid;
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_OWNER))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_GROUP)
|
|
|
|
inode->i_gid = fattr->gid;
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
|
|
|
|
inode->i_blocks = fattr->du.nfs2.blocks;
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* report the blocks in 512byte units
|
|
|
|
*/
|
|
|
|
inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
|
|
|
|
}
|
2013-05-22 20:50:44 +04:00
|
|
|
|
|
|
|
nfs_setsecurity(inode, fattr, label);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
|
2007-02-06 01:44:22 +03:00
|
|
|
nfsi->attrtimeo_timestamp = now;
|
2006-07-25 19:28:18 +04:00
|
|
|
nfsi->access_cache = RB_ROOT;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-09-27 14:20:03 +04:00
|
|
|
nfs_fscache_init_inode(inode);
|
2009-04-03 19:42:43 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
unlock_new_inode(inode);
|
|
|
|
} else
|
|
|
|
nfs_refresh_inode(inode, fattr);
|
2013-12-17 21:20:16 +04:00
|
|
|
dprintk("NFS: nfs_fhget(%s/%Lu fh_crc=0x%08x ct=%d)\n",
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 21:20:16 +04:00
|
|
|
(unsigned long long)NFS_FILEID(inode),
|
2012-03-07 06:58:20 +04:00
|
|
|
nfs_display_fhandle_hash(fh),
|
2005-04-17 02:20:36 +04:00
|
|
|
atomic_read(&inode->i_count));
|
|
|
|
|
|
|
|
out:
|
|
|
|
return inode;
|
|
|
|
|
|
|
|
out_no_inode:
|
2006-03-20 21:44:48 +03:00
|
|
|
dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode));
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out;
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_fhget);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-01-18 07:04:26 +04:00
|
|
|
#define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET|ATTR_FILE|ATTR_OPEN)
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
int
|
|
|
|
nfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
|
{
|
2015-03-18 01:25:59 +03:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2010-04-17 00:22:52 +04:00
|
|
|
struct nfs_fattr *fattr;
|
|
|
|
int error = -ENOMEM;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-03-20 21:44:14 +03:00
|
|
|
nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
|
|
|
|
|
2007-10-18 14:05:21 +04:00
|
|
|
/* skip mode change if it's just for clearing setuid/setgid */
|
|
|
|
if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
|
|
|
|
attr->ia_valid &= ~ATTR_MODE;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
if (attr->ia_valid & ATTR_SIZE) {
|
2015-01-08 12:18:30 +03:00
|
|
|
loff_t i_size;
|
|
|
|
|
2014-09-07 19:36:40 +04:00
|
|
|
BUG_ON(!S_ISREG(inode->i_mode));
|
|
|
|
|
2015-01-08 12:18:30 +03:00
|
|
|
i_size = i_size_read(inode);
|
|
|
|
if (attr->ia_size == i_size)
|
2005-04-17 02:20:36 +04:00
|
|
|
attr->ia_valid &= ~ATTR_SIZE;
|
2015-01-08 12:18:30 +03:00
|
|
|
else if (attr->ia_size < i_size && IS_SWAPFILE(inode))
|
|
|
|
return -ETXTBSY;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Optimization: if the end result is no change, don't RPC */
|
|
|
|
attr->ia_valid &= NFS_VALID_ATTRS;
|
2012-01-18 07:04:26 +04:00
|
|
|
if ((attr->ia_valid & ~(ATTR_FILE|ATTR_OPEN)) == 0)
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
|
2013-08-20 02:59:33 +04:00
|
|
|
trace_nfs_setattr_enter(inode);
|
|
|
|
|
2006-03-20 21:44:06 +03:00
|
|
|
/* Write all dirty data */
|
2015-03-25 23:38:33 +03:00
|
|
|
if (S_ISREG(inode->i_mode))
|
|
|
|
nfs_sync_inode(inode);
|
2010-04-17 00:22:52 +04:00
|
|
|
|
|
|
|
fattr = nfs_alloc_fattr();
|
|
|
|
if (fattr == NULL)
|
|
|
|
goto out;
|
2005-10-19 01:20:19 +04:00
|
|
|
/*
|
|
|
|
* Return any delegations if we're going to change ACLs
|
|
|
|
*/
|
|
|
|
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
|
2012-06-20 23:53:44 +04:00
|
|
|
NFS_PROTO(inode)->return_delegation(inode);
|
2010-04-17 00:22:52 +04:00
|
|
|
error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
|
2005-08-16 19:49:44 +04:00
|
|
|
if (error == 0)
|
2013-05-22 20:50:44 +04:00
|
|
|
error = nfs_refresh_inode(inode, fattr);
|
2010-04-17 00:22:52 +04:00
|
|
|
nfs_free_fattr(fattr);
|
|
|
|
out:
|
2013-08-20 02:59:33 +04:00
|
|
|
trace_nfs_setattr_exit(inode, error);
|
2005-08-16 19:49:44 +04:00
|
|
|
return error;
|
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_setattr);
|
2005-08-16 19:49:44 +04:00
|
|
|
|
2008-06-11 20:21:19 +04:00
|
|
|
/**
|
|
|
|
* nfs_vmtruncate - unmap mappings "freed" by truncate() syscall
|
|
|
|
* @inode: inode of the file used
|
|
|
|
* @offset: file offset to start truncating
|
|
|
|
*
|
|
|
|
* This is a copy of the common vmtruncate, but with the locking
|
|
|
|
* corrected to take into account the fact that NFS requires
|
|
|
|
* inode->i_size to be updated under the inode->i_lock.
|
2015-02-27 00:09:04 +03:00
|
|
|
* Note: must be called with inode->i_lock held!
|
2008-06-11 20:21:19 +04:00
|
|
|
*/
|
|
|
|
static int nfs_vmtruncate(struct inode * inode, loff_t offset)
|
|
|
|
{
|
2009-08-20 20:35:06 +04:00
|
|
|
int err;
|
2008-06-11 20:21:19 +04:00
|
|
|
|
2009-08-20 20:35:06 +04:00
|
|
|
err = inode_newsize_ok(inode, offset);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2008-06-11 20:21:19 +04:00
|
|
|
|
2009-08-20 20:35:06 +04:00
|
|
|
i_size_write(inode, offset);
|
2014-06-20 21:11:01 +04:00
|
|
|
/* Optimisation */
|
|
|
|
if (offset == 0)
|
|
|
|
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA;
|
2009-08-20 20:35:06 +04:00
|
|
|
|
2015-02-27 00:09:04 +03:00
|
|
|
spin_unlock(&inode->i_lock);
|
2013-09-13 02:13:56 +04:00
|
|
|
truncate_pagecache(inode, offset);
|
2015-02-27 00:09:04 +03:00
|
|
|
spin_lock(&inode->i_lock);
|
2009-08-20 20:35:06 +04:00
|
|
|
out:
|
|
|
|
return err;
|
2008-06-11 20:21:19 +04:00
|
|
|
}
|
|
|
|
|
2005-08-16 19:49:44 +04:00
|
|
|
/**
|
|
|
|
* nfs_setattr_update_inode - Update inode metadata after a setattr call.
|
|
|
|
* @inode: pointer to struct inode
|
|
|
|
* @attr: pointer to struct iattr
|
|
|
|
*
|
|
|
|
* Note: we do this in the *proc.c in order to ensure that
|
|
|
|
* it works for things like exclusive creates too.
|
|
|
|
*/
|
2015-02-27 00:09:04 +03:00
|
|
|
void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
|
|
|
|
struct nfs_fattr *fattr)
|
2005-08-16 19:49:44 +04:00
|
|
|
{
|
2015-02-27 00:09:04 +03:00
|
|
|
/* Barrier: bump the attribute generation count. */
|
|
|
|
nfs_fattr_set_barrier(fattr);
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
NFS_I(inode)->attr_gencount = fattr->gencount;
|
2005-08-16 19:49:44 +04:00
|
|
|
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
|
2005-04-17 02:20:36 +04:00
|
|
|
if ((attr->ia_valid & ATTR_MODE) != 0) {
|
2005-08-16 19:49:44 +04:00
|
|
|
int mode = attr->ia_mode & S_IALLUGO;
|
|
|
|
mode |= inode->i_mode & ~S_IALLUGO;
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_mode = mode;
|
|
|
|
}
|
|
|
|
if ((attr->ia_valid & ATTR_UID) != 0)
|
|
|
|
inode->i_uid = attr->ia_uid;
|
|
|
|
if ((attr->ia_valid & ATTR_GID) != 0)
|
|
|
|
inode->i_gid = attr->ia_gid;
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL);
|
2005-08-16 19:49:44 +04:00
|
|
|
}
|
|
|
|
if ((attr->ia_valid & ATTR_SIZE) != 0) {
|
2006-03-20 21:44:14 +03:00
|
|
|
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
|
2008-06-11 20:21:19 +04:00
|
|
|
nfs_vmtruncate(inode, attr->ia_size);
|
2005-08-16 19:49:44 +04:00
|
|
|
}
|
2015-02-27 00:09:04 +03:00
|
|
|
nfs_update_inode(inode, fattr);
|
|
|
|
spin_unlock(&inode->i_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-02-08 02:02:08 +04:00
|
|
|
static void nfs_request_parent_use_readdirplus(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct dentry *parent;
|
|
|
|
|
|
|
|
parent = dget_parent(dentry);
|
2015-03-18 01:25:59 +03:00
|
|
|
nfs_force_use_readdirplus(d_inode(parent));
|
2014-02-08 02:02:08 +04:00
|
|
|
dput(parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nfs_need_revalidate_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
if (NFS_I(inode)->cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
|
|
|
|
return true;
|
|
|
|
if (nfs_attribute_cache_expired(inode))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
|
|
|
|
{
|
2015-03-18 01:25:59 +03:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2005-08-18 22:24:09 +04:00
|
|
|
int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
|
2014-10-23 16:02:47 +04:00
|
|
|
int err = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-08-20 02:59:33 +04:00
|
|
|
trace_nfs_getattr_enter(inode);
|
2010-02-20 04:03:26 +03:00
|
|
|
/* Flush out writes to the server in order to update c/mtime. */
|
2007-10-26 21:32:13 +04:00
|
|
|
if (S_ISREG(inode->i_mode)) {
|
2015-03-25 23:40:07 +03:00
|
|
|
mutex_lock(&inode->i_mutex);
|
2015-03-25 23:38:33 +03:00
|
|
|
err = nfs_sync_inode(inode);
|
2015-03-25 23:40:07 +03:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
2010-02-20 04:03:26 +03:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2007-10-26 21:32:13 +04:00
|
|
|
}
|
2006-01-10 07:52:17 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We may force a getattr if the user cares about atime.
|
|
|
|
*
|
|
|
|
* Note that we only have to check the vfsmount flags here:
|
|
|
|
* - NFS always sets S_NOATIME by so checking it would give a
|
|
|
|
* bogus result
|
|
|
|
* - NFS never sets MS_NOATIME or MS_NODIRATIME so there is
|
|
|
|
* no point in checking those.
|
|
|
|
*/
|
|
|
|
if ((mnt->mnt_flags & MNT_NOATIME) ||
|
|
|
|
((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
|
2005-04-17 02:20:36 +04:00
|
|
|
need_atime = 0;
|
2006-01-10 07:52:17 +03:00
|
|
|
|
2014-02-08 02:02:08 +04:00
|
|
|
if (need_atime || nfs_need_revalidate_inode(inode)) {
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
|
|
|
|
|
|
|
if (server->caps & NFS_CAP_READDIRPLUS)
|
|
|
|
nfs_request_parent_use_readdirplus(dentry);
|
|
|
|
err = __nfs_revalidate_inode(server, inode);
|
|
|
|
}
|
2007-08-03 23:07:10 +04:00
|
|
|
if (!err) {
|
2005-04-17 02:20:36 +04:00
|
|
|
generic_fillattr(inode, stat);
|
2007-10-09 20:01:04 +04:00
|
|
|
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
|
2007-08-03 23:07:10 +04:00
|
|
|
}
|
2010-02-20 04:03:26 +03:00
|
|
|
out:
|
2013-08-20 02:59:33 +04:00
|
|
|
trace_nfs_getattr_exit(inode, err);
|
2005-04-17 02:20:36 +04:00
|
|
|
return err;
|
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_getattr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2010-06-26 00:35:53 +04:00
|
|
|
static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
|
|
|
|
{
|
|
|
|
atomic_set(&l_ctx->count, 1);
|
2012-08-14 02:54:45 +04:00
|
|
|
l_ctx->lockowner.l_owner = current->files;
|
|
|
|
l_ctx->lockowner.l_pid = current->tgid;
|
2010-06-26 00:35:53 +04:00
|
|
|
INIT_LIST_HEAD(&l_ctx->list);
|
2013-04-09 05:38:12 +04:00
|
|
|
nfs_iocounter_init(&l_ctx->io_count);
|
2010-06-26 00:35:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
|
|
|
|
{
|
2013-03-16 02:11:31 +04:00
|
|
|
struct nfs_lock_context *head = &ctx->lock_context;
|
|
|
|
struct nfs_lock_context *pos = head;
|
2010-06-26 00:35:53 +04:00
|
|
|
|
2013-03-16 02:11:31 +04:00
|
|
|
do {
|
2012-08-14 02:54:45 +04:00
|
|
|
if (pos->lockowner.l_owner != current->files)
|
2010-06-26 00:35:53 +04:00
|
|
|
continue;
|
2012-08-14 02:54:45 +04:00
|
|
|
if (pos->lockowner.l_pid != current->tgid)
|
2010-06-26 00:35:53 +04:00
|
|
|
continue;
|
|
|
|
atomic_inc(&pos->count);
|
|
|
|
return pos;
|
2013-03-16 02:11:31 +04:00
|
|
|
} while ((pos = list_entry(pos->list.next, typeof(*pos), list)) != head);
|
2010-06-26 00:35:53 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx)
|
|
|
|
{
|
|
|
|
struct nfs_lock_context *res, *new = NULL;
|
2015-03-18 01:25:59 +03:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2010-06-26 00:35:53 +04:00
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
res = __nfs_find_lock_context(ctx);
|
|
|
|
if (res == NULL) {
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
new = kmalloc(sizeof(*new), GFP_KERNEL);
|
|
|
|
if (new == NULL)
|
2012-08-14 01:15:50 +04:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2010-06-26 00:35:53 +04:00
|
|
|
nfs_init_lock_context(new);
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
res = __nfs_find_lock_context(ctx);
|
|
|
|
if (res == NULL) {
|
|
|
|
list_add_tail(&new->list, &ctx->lock_context.list);
|
|
|
|
new->open_context = ctx;
|
|
|
|
res = new;
|
|
|
|
new = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
kfree(new);
|
|
|
|
return res;
|
|
|
|
}
|
2014-09-26 21:58:48 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_get_lock_context);
|
2010-06-26 00:35:53 +04:00
|
|
|
|
|
|
|
void nfs_put_lock_context(struct nfs_lock_context *l_ctx)
|
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx = l_ctx->open_context;
|
2015-03-18 01:25:59 +03:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2010-06-26 00:35:53 +04:00
|
|
|
|
|
|
|
if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock))
|
|
|
|
return;
|
|
|
|
list_del(&l_ctx->list);
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
kfree(l_ctx);
|
|
|
|
}
|
2014-09-26 21:58:48 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_put_lock_context);
|
2010-06-26 00:35:53 +04:00
|
|
|
|
2009-03-19 22:35:50 +03:00
|
|
|
/**
|
|
|
|
* nfs_close_context - Common close_context() routine NFSv2/v3
|
|
|
|
* @ctx: pointer to context
|
|
|
|
* @is_sync: is this a synchronous close
|
|
|
|
*
|
|
|
|
* always ensure that the attributes are up to date if we're mounted
|
|
|
|
* with close-to-open semantics
|
|
|
|
*/
|
|
|
|
void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
|
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct nfs_server *server;
|
|
|
|
|
|
|
|
if (!(ctx->mode & FMODE_WRITE))
|
|
|
|
return;
|
|
|
|
if (!is_sync)
|
|
|
|
return;
|
2015-03-18 01:25:59 +03:00
|
|
|
inode = d_inode(ctx->dentry);
|
2009-03-19 22:35:50 +03:00
|
|
|
if (!list_empty(&NFS_I(inode)->open_files))
|
|
|
|
return;
|
|
|
|
server = NFS_SERVER(inode);
|
|
|
|
if (server->flags & NFS_MOUNT_NOCTO)
|
|
|
|
return;
|
|
|
|
nfs_revalidate_inode(server, inode);
|
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_close_context);
|
2009-03-19 22:35:50 +03:00
|
|
|
|
2011-10-24 02:49:54 +04:00
|
|
|
struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f_mode)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx;
|
2011-10-24 02:49:54 +04:00
|
|
|
struct rpc_cred *cred = rpc_lookup_cred();
|
|
|
|
if (IS_ERR(cred))
|
|
|
|
return ERR_CAST(cred);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-09-27 12:49:39 +04:00
|
|
|
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
|
2011-10-24 02:49:54 +04:00
|
|
|
if (!ctx) {
|
|
|
|
put_rpccred(cred);
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2011-10-24 02:49:54 +04:00
|
|
|
nfs_sb_active(dentry->d_sb);
|
|
|
|
ctx->dentry = dget(dentry);
|
|
|
|
ctx->cred = cred;
|
|
|
|
ctx->state = NULL;
|
|
|
|
ctx->mode = f_mode;
|
|
|
|
ctx->flags = 0;
|
|
|
|
ctx->error = 0;
|
|
|
|
nfs_init_lock_context(&ctx->lock_context);
|
|
|
|
ctx->lock_context.open_context = ctx;
|
|
|
|
INIT_LIST_HEAD(&ctx->list);
|
2012-05-23 13:02:35 +04:00
|
|
|
ctx->mdsthreshold = NULL;
|
2005-04-17 02:20:36 +04:00
|
|
|
return ctx;
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(alloc_nfs_open_context);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
|
|
|
|
{
|
|
|
|
if (ctx != NULL)
|
2010-06-26 00:35:53 +04:00
|
|
|
atomic_inc(&ctx->lock_context.count);
|
2005-04-17 02:20:36 +04:00
|
|
|
return ctx;
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(get_nfs_open_context);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-03-19 22:35:50 +03:00
|
|
|
static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2015-03-18 01:25:59 +03:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2011-06-23 02:40:12 +04:00
|
|
|
struct super_block *sb = ctx->dentry->d_sb;
|
2007-06-18 00:02:44 +04:00
|
|
|
|
2010-09-27 23:51:20 +04:00
|
|
|
if (!list_empty(&ctx->list)) {
|
2010-09-23 20:22:09 +04:00
|
|
|
if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock))
|
|
|
|
return;
|
|
|
|
list_del(&ctx->list);
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
} else if (!atomic_dec_and_test(&ctx->lock_context.count))
|
2007-07-26 20:06:17 +04:00
|
|
|
return;
|
2010-09-27 23:51:20 +04:00
|
|
|
if (inode != NULL)
|
|
|
|
NFS_PROTO(inode)->close_context(ctx, is_sync);
|
2007-06-18 00:02:44 +04:00
|
|
|
if (ctx->cred != NULL)
|
|
|
|
put_rpccred(ctx->cred);
|
2011-06-23 02:40:12 +04:00
|
|
|
dput(ctx->dentry);
|
2013-01-12 01:39:51 +04:00
|
|
|
nfs_sb_deactive(sb);
|
2012-05-23 13:02:35 +04:00
|
|
|
kfree(ctx->mdsthreshold);
|
2007-06-18 00:02:44 +04:00
|
|
|
kfree(ctx);
|
|
|
|
}
|
|
|
|
|
2007-10-19 02:03:27 +04:00
|
|
|
void put_nfs_open_context(struct nfs_open_context *ctx)
|
|
|
|
{
|
|
|
|
__put_nfs_open_context(ctx, 0);
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(put_nfs_open_context);
|
2007-10-19 02:03:27 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Ensure that mmap has a recent RPC credential for use when writing out
|
|
|
|
* shared pages
|
|
|
|
*/
|
2013-05-29 21:34:46 +04:00
|
|
|
void nfs_inode_attach_open_context(struct nfs_open_context *ctx)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2015-03-18 01:25:59 +03:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2005-04-17 02:20:36 +04:00
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
list_add(&ctx->list, &nfsi->open_files);
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
2013-05-29 21:34:46 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_inode_attach_open_context);
|
|
|
|
|
|
|
|
void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
|
|
|
|
{
|
|
|
|
filp->private_data = get_nfs_open_context(ctx);
|
|
|
|
if (list_empty(&ctx->list))
|
|
|
|
nfs_inode_attach_open_context(ctx);
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_file_set_open_context);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-11-04 23:33:38 +03:00
|
|
|
/*
|
|
|
|
* Given an inode, search for an open context with the desired characteristics
|
|
|
|
*/
|
2008-12-23 23:21:56 +03:00
|
|
|
struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
struct nfs_open_context *pos, *ctx = NULL;
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
list_for_each_entry(pos, &nfsi->open_files, list) {
|
2005-11-04 23:33:38 +03:00
|
|
|
if (cred != NULL && pos->cred != cred)
|
|
|
|
continue;
|
2010-03-25 20:54:49 +03:00
|
|
|
if ((pos->mode & (FMODE_READ|FMODE_WRITE)) != mode)
|
|
|
|
continue;
|
|
|
|
ctx = get_nfs_open_context(pos);
|
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
2006-03-20 21:44:03 +03:00
|
|
|
static void nfs_file_clear_open_context(struct file *filp)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-08-11 01:44:32 +04:00
|
|
|
struct nfs_open_context *ctx = nfs_file_open_context(filp);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (ctx) {
|
2015-03-18 01:25:59 +03:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2013-05-29 21:34:46 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
filp->private_data = NULL;
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
|
|
|
|
spin_unlock(&inode->i_lock);
|
2010-02-01 22:17:50 +03:00
|
|
|
__put_nfs_open_context(ctx, filp->f_flags & O_DIRECT ? 0 : 1);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These allocate and release file read/write context information.
|
|
|
|
*/
|
|
|
|
int nfs_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
|
2011-10-24 02:49:54 +04:00
|
|
|
ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
|
|
|
|
if (IS_ERR(ctx))
|
|
|
|
return PTR_ERR(ctx);
|
2005-04-17 02:20:36 +04:00
|
|
|
nfs_file_set_open_context(filp, ctx);
|
|
|
|
put_nfs_open_context(ctx);
|
2013-09-27 14:20:03 +04:00
|
|
|
nfs_fscache_open_file(inode, filp);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfs_release(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
nfs_file_clear_open_context(filp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is called whenever some part of NFS notices that
|
|
|
|
* the cached attributes have to be refreshed.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
__nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
|
|
|
|
{
|
|
|
|
int status = -ESTALE;
|
2013-05-22 20:50:42 +04:00
|
|
|
struct nfs4_label *label = NULL;
|
2010-04-17 00:22:49 +04:00
|
|
|
struct nfs_fattr *fattr = NULL;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
2013-12-17 21:20:16 +04:00
|
|
|
dfprintk(PAGECACHE, "NFS: revalidating (%s/%Lu)\n",
|
|
|
|
inode->i_sb->s_id, (unsigned long long)NFS_FILEID(inode));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-08-20 02:59:33 +04:00
|
|
|
trace_nfs_revalidate_inode_enter(inode);
|
|
|
|
|
2006-10-20 10:28:42 +04:00
|
|
|
if (is_bad_inode(inode))
|
2008-10-05 22:48:22 +04:00
|
|
|
goto out;
|
2005-04-17 02:20:36 +04:00
|
|
|
if (NFS_STALE(inode))
|
2005-08-18 22:24:11 +04:00
|
|
|
goto out;
|
2007-09-29 03:11:33 +04:00
|
|
|
|
2010-04-17 00:22:49 +04:00
|
|
|
status = -ENOMEM;
|
|
|
|
fattr = nfs_alloc_fattr();
|
|
|
|
if (fattr == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2008-10-05 22:48:22 +04:00
|
|
|
nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
|
2013-05-22 20:50:43 +04:00
|
|
|
|
|
|
|
label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
|
|
|
|
if (IS_ERR(label)) {
|
|
|
|
status = PTR_ERR(label);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-05-22 20:50:42 +04:00
|
|
|
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, label);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (status != 0) {
|
2013-12-17 21:20:16 +04:00
|
|
|
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) getattr failed, error=%d\n",
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 21:20:16 +04:00
|
|
|
(unsigned long long)NFS_FILEID(inode), status);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (status == -ESTALE) {
|
|
|
|
nfs_zap_caches(inode);
|
|
|
|
if (!S_ISDIR(inode->i_mode))
|
2008-01-23 09:58:59 +03:00
|
|
|
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2013-05-22 20:50:43 +04:00
|
|
|
goto err_out;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2010-04-17 00:22:49 +04:00
|
|
|
status = nfs_refresh_inode(inode, fattr);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (status) {
|
2013-12-17 21:20:16 +04:00
|
|
|
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) refresh failed, error=%d\n",
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 21:20:16 +04:00
|
|
|
(unsigned long long)NFS_FILEID(inode), status);
|
2013-05-22 20:50:43 +04:00
|
|
|
goto err_out;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2005-08-18 22:24:09 +04:00
|
|
|
|
2005-12-03 23:20:07 +03:00
|
|
|
if (nfsi->cache_validity & NFS_INO_INVALID_ACL)
|
2005-06-22 21:16:22 +04:00
|
|
|
nfs_zap_acl_cache(inode);
|
2005-08-18 22:24:09 +04:00
|
|
|
|
2013-11-02 14:57:18 +04:00
|
|
|
nfs_setsecurity(inode, fattr, label);
|
|
|
|
|
2013-12-17 21:20:16 +04:00
|
|
|
dfprintk(PAGECACHE, "NFS: (%s/%Lu) revalidation complete\n",
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 21:20:16 +04:00
|
|
|
(unsigned long long)NFS_FILEID(inode));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-05-22 20:50:43 +04:00
|
|
|
err_out:
|
|
|
|
nfs4_label_free(label);
|
|
|
|
out:
|
2010-04-17 00:22:49 +04:00
|
|
|
nfs_free_fattr(fattr);
|
2013-08-20 02:59:33 +04:00
|
|
|
trace_nfs_revalidate_inode_exit(inode, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfs_attribute_timeout(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
2010-04-17 00:42:46 +04:00
|
|
|
return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
|
|
|
|
}
|
|
|
|
|
2013-07-06 01:49:30 +04:00
|
|
|
int nfs_attribute_cache_expired(struct inode *inode)
|
2010-04-17 00:42:46 +04:00
|
|
|
{
|
2010-03-10 23:21:44 +03:00
|
|
|
if (nfs_have_delegated_attributes(inode))
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
2010-04-17 00:42:46 +04:00
|
|
|
return nfs_attribute_timeout(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs_revalidate_inode - Revalidate the inode attributes
|
|
|
|
* @server - pointer to nfs_server struct
|
|
|
|
* @inode - pointer to inode struct
|
|
|
|
*
|
|
|
|
* Updates inode attribute information by retrieving the data from the server.
|
|
|
|
*/
|
|
|
|
int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
|
|
|
|
{
|
2014-02-08 02:02:08 +04:00
|
|
|
if (!nfs_need_revalidate_inode(inode))
|
2005-04-17 02:20:36 +04:00
|
|
|
return NFS_STALE(inode) ? -ESTALE : 0;
|
|
|
|
return __nfs_revalidate_inode(server, inode);
|
|
|
|
}
|
2012-07-31 00:05:24 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-07-14 05:28:20 +04:00
|
|
|
int nfs_revalidate_inode_rcu(struct nfs_server *server, struct inode *inode)
|
|
|
|
{
|
|
|
|
if (!(NFS_I(inode)->cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
|
|
|
|
&& !nfs_attribute_cache_expired(inode))
|
|
|
|
return NFS_STALE(inode) ? -ESTALE : 0;
|
|
|
|
return -ECHILD;
|
|
|
|
}
|
|
|
|
|
2010-02-20 04:03:30 +03:00
|
|
|
static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
|
2007-01-24 22:54:55 +03:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2013-08-05 21:26:31 +04:00
|
|
|
int ret;
|
|
|
|
|
2007-01-24 22:54:55 +03:00
|
|
|
if (mapping->nrpages != 0) {
|
2013-08-05 21:26:31 +04:00
|
|
|
if (S_ISREG(inode->i_mode)) {
|
2015-03-03 08:06:35 +03:00
|
|
|
unmap_mapping_range(mapping, 0, 0, 0);
|
2013-08-05 21:26:31 +04:00
|
|
|
ret = nfs_sync_mapping(mapping);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = invalidate_inode_pages2(mapping);
|
2007-01-24 22:54:55 +03:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
if (S_ISDIR(inode->i_mode)) {
|
|
|
|
spin_lock(&inode->i_lock);
|
2007-01-24 22:54:55 +03:00
|
|
|
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
2007-01-24 22:54:55 +03:00
|
|
|
nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
|
2012-12-21 01:52:38 +04:00
|
|
|
nfs_fscache_wait_on_invalidate(inode);
|
2013-08-20 02:59:33 +04:00
|
|
|
|
2013-12-17 21:20:16 +04:00
|
|
|
dfprintk(PAGECACHE, "NFS: (%s/%Lu) data cache invalidated\n",
|
|
|
|
inode->i_sb->s_id,
|
|
|
|
(unsigned long long)NFS_FILEID(inode));
|
2007-01-24 22:54:55 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-29 19:23:50 +04:00
|
|
|
static bool nfs_mapping_need_revalidate_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
if (nfs_have_delegated_attributes(inode))
|
|
|
|
return false;
|
|
|
|
return (NFS_I(inode)->cache_validity & NFS_INO_REVAL_PAGECACHE)
|
|
|
|
|| nfs_attribute_timeout(inode)
|
|
|
|
|| NFS_STALE(inode);
|
|
|
|
}
|
|
|
|
|
2007-01-24 22:54:55 +03:00
|
|
|
/**
|
2015-03-03 07:32:08 +03:00
|
|
|
* __nfs_revalidate_mapping - Revalidate the pagecache
|
2007-01-24 22:54:55 +03:00
|
|
|
* @inode - pointer to host inode
|
|
|
|
* @mapping - pointer to mapping
|
2015-03-03 07:32:08 +03:00
|
|
|
* @may_lock - take inode->i_mutex?
|
2007-01-24 22:54:55 +03:00
|
|
|
*/
|
2015-03-03 07:32:08 +03:00
|
|
|
static int __nfs_revalidate_mapping(struct inode *inode,
|
|
|
|
struct address_space *mapping,
|
|
|
|
bool may_lock)
|
2007-01-24 22:54:55 +03:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
unsigned long *bitlock = &nfsi->flags;
|
2007-01-24 22:54:55 +03:00
|
|
|
int ret = 0;
|
2005-08-18 22:24:12 +04:00
|
|
|
|
2012-08-01 03:45:10 +04:00
|
|
|
/* swapfiles are not supposed to be shared. */
|
|
|
|
if (IS_SWAPFILE(inode))
|
|
|
|
goto out;
|
|
|
|
|
2012-04-29 19:23:50 +04:00
|
|
|
if (nfs_mapping_need_revalidate_inode(inode)) {
|
2007-01-24 22:54:55 +03:00
|
|
|
ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2005-06-22 21:16:30 +04:00
|
|
|
}
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We must clear NFS_INO_INVALID_DATA first to ensure that
|
|
|
|
* invalidations that come in while we're shooting down the mappings
|
|
|
|
* are respected. But, that leaves a race window where one revalidator
|
|
|
|
* can clear the flag, and then another checks it before the mapping
|
|
|
|
* gets invalidated. Fix that by serializing access to this part of
|
|
|
|
* the function.
|
|
|
|
*
|
|
|
|
* At the same time, we need to allow other tasks to see whether we
|
|
|
|
* might be in the middle of invalidating the pages, so we only set
|
|
|
|
* the bit lock here if it looks like we're going to be doing that.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
sched: Remove proliferation of wait_on_bit() action functions
The current "wait_on_bit" interface requires an 'action'
function to be provided which does the actual waiting.
There are over 20 such functions, many of them identical.
Most cases can be satisfied by one of just two functions, one
which uses io_schedule() and one which just uses schedule().
So:
Rename wait_on_bit and wait_on_bit_lock to
wait_on_bit_action and wait_on_bit_lock_action
to make it explicit that they need an action function.
Introduce new wait_on_bit{,_lock} and wait_on_bit{,_lock}_io
which are *not* given an action function but implicitly use
a standard one.
The decision to error-out if a signal is pending is now made
based on the 'mode' argument rather than being encoded in the action
function.
All instances of the old wait_on_bit and wait_on_bit_lock which
can use the new version have been changed accordingly and their
action functions have been discarded.
wait_on_bit{_lock} does not return any specific error code in the
event of a signal so the caller must check for non-zero and
interpolate their own error code as appropriate.
The wait_on_bit() call in __fscache_wait_on_invalidate() was
ambiguous as it specified TASK_UNINTERRUPTIBLE but used
fscache_wait_bit_interruptible as an action function.
David Howells confirms this should be uniformly
"uninterruptible"
The main remaining user of wait_on_bit{,_lock}_action is NFS
which needs to use a freezer-aware schedule() call.
A comment in fs/gfs2/glock.c notes that having multiple 'action'
functions is useful as they display differently in the 'wchan'
field of 'ps'. (and /proc/$PID/wchan).
As the new bit_wait{,_io} functions are tagged "__sched", they
will not show up at all, but something higher in the stack. So
the distinction will still be visible, only with different
function names (gds2_glock_wait versus gfs2_glock_dq_wait in the
gfs2/glock.c case).
Since first version of this patch (against 3.15) two new action
functions appeared, on in NFS and one in CIFS. CIFS also now
uses an action function that makes the same freezer aware
schedule call as NFS.
Signed-off-by: NeilBrown <neilb@suse.de>
Acked-by: David Howells <dhowells@redhat.com> (fscache, keys)
Acked-by: Steven Whitehouse <swhiteho@redhat.com> (gfs2)
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Steve French <sfrench@samba.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/20140707051603.28027.72349.stgit@notabene.brown
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2014-07-07 09:16:04 +04:00
|
|
|
ret = wait_on_bit_action(bitlock, NFS_INO_INVALIDATING,
|
|
|
|
nfs_wait_bit_killable, TASK_KILLABLE);
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2014-01-28 18:37:16 +04:00
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
if (test_bit(NFS_INO_INVALIDATING, bitlock)) {
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
break;
|
|
|
|
spin_unlock(&inode->i_lock);
|
2014-01-28 18:37:16 +04:00
|
|
|
goto out;
|
2013-08-20 02:59:33 +04:00
|
|
|
}
|
|
|
|
|
2014-01-28 18:37:16 +04:00
|
|
|
set_bit(NFS_INO_INVALIDATING, bitlock);
|
2014-01-28 22:47:46 +04:00
|
|
|
smp_wmb();
|
2014-01-28 18:37:16 +04:00
|
|
|
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
trace_nfs_invalidate_mapping_enter(inode);
|
2015-03-03 07:32:08 +03:00
|
|
|
if (may_lock) {
|
|
|
|
mutex_lock(&inode->i_mutex);
|
|
|
|
ret = nfs_invalidate_mapping(inode, mapping);
|
|
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
} else
|
|
|
|
ret = nfs_invalidate_mapping(inode, mapping);
|
2014-01-28 18:37:16 +04:00
|
|
|
trace_nfs_invalidate_mapping_exit(inode, ret);
|
|
|
|
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
clear_bit_unlock(NFS_INO_INVALIDATING, bitlock);
|
2014-03-17 21:06:10 +04:00
|
|
|
smp_mb__after_atomic();
|
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
There is a possible race in how the nfs_invalidate_mapping function is
handled. Currently, we go and invalidate the pages in the file and then
clear NFS_INO_INVALID_DATA.
The problem is that it's possible for a stale page to creep into the
mapping after the page was invalidated (i.e., via readahead). If another
writer comes along and sets the flag after that happens but before
invalidate_inode_pages2 returns then we could clear the flag
without the cache having been properly invalidated.
So, we must clear the flag first and then invalidate the pages. Doing
this however, opens another race:
It's possible to have two concurrent read() calls that end up in
nfs_revalidate_mapping at the same time. The first one clears the
NFS_INO_INVALID_DATA flag and then goes to call nfs_invalidate_mapping.
Just before calling that though, the other task races in, checks the
flag and finds it cleared. At that point, it trusts that the mapping is
good and gets the lock on the page, allowing the read() to be satisfied
from the cache even though the data is no longer valid.
These effects are easily manifested by running diotest3 from the LTP
test suite on NFS. That program does a series of DIO writes and buffered
reads. The operations are serialized and page-aligned but the existing
code fails the test since it occasionally allows a read to come out of
the cache incorrectly. While mixing direct and buffered I/O isn't
recommended, I believe it's possible to hit this in other ways that just
use buffered I/O, though that situation is much harder to reproduce.
The problem is that the checking/clearing of that flag and the
invalidation of the mapping really need to be atomic. Fix this by
serializing concurrent invalidations with a bitlock.
At the same time, we also need to allow other places that check
NFS_INO_INVALID_DATA to check whether we might be in the middle of
invalidating the file, so fix up a couple of places that do that
to look for the new NFS_INO_INVALIDATING flag.
Doing this requires us to be careful not to set the bitlock
unnecessarily, so this code only does that if it believes it will
be doing an invalidation.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2014-01-27 22:46:15 +04:00
|
|
|
wake_up_bit(bitlock, NFS_INO_INVALIDATING);
|
2006-10-20 10:28:40 +04:00
|
|
|
out:
|
2006-05-25 09:40:59 +04:00
|
|
|
return ret;
|
2005-06-22 21:16:30 +04:00
|
|
|
}
|
|
|
|
|
2015-03-03 07:32:08 +03:00
|
|
|
/**
|
|
|
|
* nfs_revalidate_mapping - Revalidate the pagecache
|
|
|
|
* @inode - pointer to host inode
|
|
|
|
* @mapping - pointer to mapping
|
|
|
|
*/
|
|
|
|
int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
|
|
|
|
{
|
|
|
|
return __nfs_revalidate_mapping(inode, mapping, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs_revalidate_mapping_protected - Revalidate the pagecache
|
|
|
|
* @inode - pointer to host inode
|
|
|
|
* @mapping - pointer to mapping
|
|
|
|
*
|
|
|
|
* Differs from nfs_revalidate_mapping() in that it grabs the inode->i_mutex
|
|
|
|
* while invalidating the mapping.
|
|
|
|
*/
|
|
|
|
int nfs_revalidate_mapping_protected(struct inode *inode, struct address_space *mapping)
|
|
|
|
{
|
|
|
|
return __nfs_revalidate_mapping(inode, mapping, true);
|
|
|
|
}
|
|
|
|
|
2011-01-25 23:28:21 +03:00
|
|
|
static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
2006-01-03 11:55:40 +03:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2011-01-25 23:28:21 +03:00
|
|
|
unsigned long ret = 0;
|
2006-01-03 11:55:40 +03:00
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE)
|
|
|
|
&& (fattr->valid & NFS_ATTR_FATTR_CHANGE)
|
2011-10-18 03:08:46 +04:00
|
|
|
&& inode->i_version == fattr->pre_change_attr) {
|
|
|
|
inode->i_version = fattr->change_attr;
|
2007-09-30 23:21:24 +04:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
|
2011-01-25 23:28:21 +03:00
|
|
|
ret |= NFS_INO_INVALID_ATTR;
|
2007-09-30 23:21:24 +04:00
|
|
|
}
|
2006-01-03 11:55:40 +03:00
|
|
|
/* If we have atomic WCC data, we may update some attributes */
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME)
|
|
|
|
&& (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
2011-01-25 23:28:21 +03:00
|
|
|
&& timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) {
|
|
|
|
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
|
|
|
|
ret |= NFS_INO_INVALID_ATTR;
|
|
|
|
}
|
2009-03-11 21:10:24 +03:00
|
|
|
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME)
|
|
|
|
&& (fattr->valid & NFS_ATTR_FATTR_MTIME)
|
|
|
|
&& timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) {
|
2011-01-25 23:28:21 +03:00
|
|
|
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
|
2011-01-25 23:28:21 +03:00
|
|
|
ret |= NFS_INO_INVALID_ATTR;
|
2006-01-03 11:55:40 +03:00
|
|
|
}
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_PRESIZE)
|
|
|
|
&& (fattr->valid & NFS_ATTR_FATTR_SIZE)
|
|
|
|
&& i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size)
|
2014-11-12 20:08:00 +03:00
|
|
|
&& nfsi->nrequests == 0) {
|
2011-01-25 23:28:21 +03:00
|
|
|
i_size_write(inode, nfs_size_to_loff_t(fattr->size));
|
|
|
|
ret |= NFS_INO_INVALID_ATTR;
|
|
|
|
}
|
2012-12-21 01:52:38 +04:00
|
|
|
|
2011-01-25 23:28:21 +03:00
|
|
|
return ret;
|
2006-01-03 11:55:40 +03:00
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/**
|
2005-10-28 06:12:39 +04:00
|
|
|
* nfs_check_inode_attributes - verify consistency of the inode attribute cache
|
2005-04-17 02:20:36 +04:00
|
|
|
* @inode - pointer to inode
|
|
|
|
* @fattr - updated attributes
|
|
|
|
*
|
|
|
|
* Verifies the attribute cache. If we have just changed the attributes,
|
|
|
|
* so that fattr carries weak cache consistency data, then it may
|
|
|
|
* also update the ctime/mtime/change_attribute.
|
|
|
|
*/
|
2005-10-28 06:12:39 +04:00
|
|
|
static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
loff_t cur_size, new_isize;
|
2007-10-08 22:26:13 +04:00
|
|
|
unsigned long invalid = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-08-18 22:24:12 +04:00
|
|
|
|
2012-04-29 20:30:19 +04:00
|
|
|
if (nfs_have_delegated_attributes(inode))
|
|
|
|
return 0;
|
2006-03-20 21:44:07 +03:00
|
|
|
/* Has the inode gone and changed behind our back? */
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
|
|
|
|
return -EIO;
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
|
2006-03-20 21:44:07 +03:00
|
|
|
return -EIO;
|
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
|
2011-10-18 03:08:46 +04:00
|
|
|
inode->i_version != fattr->change_attr)
|
2007-10-08 22:26:13 +04:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Verify a few of the more important attributes */
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime))
|
2012-04-27 21:48:18 +04:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR;
|
2006-03-20 21:44:07 +03:00
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
|
|
|
|
cur_size = i_size_read(inode);
|
|
|
|
new_isize = nfs_size_to_loff_t(fattr->size);
|
2014-11-12 20:08:00 +03:00
|
|
|
if (cur_size != new_isize && nfsi->nrequests == 0)
|
2009-03-11 21:10:24 +03:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Have any file permissions changed? */
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO))
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
|
2013-02-02 02:26:23 +04:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && !uid_eq(inode->i_uid, fattr->uid))
|
2009-03-11 21:10:24 +03:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
|
2013-02-02 02:26:23 +04:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && !gid_eq(inode->i_gid, fattr->gid))
|
2007-10-08 22:26:13 +04:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Has the link count changed? */
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_NLINK) && inode->i_nlink != fattr->nlink)
|
2007-10-08 22:26:13 +04:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_ATIME) && !timespec_equal(&inode->i_atime, &fattr->atime))
|
2007-10-08 22:26:13 +04:00
|
|
|
invalid |= NFS_INO_INVALID_ATIME;
|
|
|
|
|
|
|
|
if (invalid != 0)
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, invalid);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-10-28 06:12:39 +04:00
|
|
|
nfsi->read_cache_jiffies = fattr->time_start;
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-24 01:28:41 +04:00
|
|
|
static int nfs_ctime_need_update(const struct inode *inode, const struct nfs_fattr *fattr)
|
2008-10-05 20:07:23 +04:00
|
|
|
{
|
2009-03-11 21:10:24 +03:00
|
|
|
if (!(fattr->valid & NFS_ATTR_FATTR_CTIME))
|
|
|
|
return 0;
|
2008-09-24 01:28:41 +04:00
|
|
|
return timespec_compare(&fattr->ctime, &inode->i_ctime) > 0;
|
|
|
|
}
|
|
|
|
|
2008-10-28 22:21:40 +03:00
|
|
|
static atomic_long_t nfs_attr_generation_counter;
|
2008-10-15 03:16:07 +04:00
|
|
|
|
|
|
|
static unsigned long nfs_read_attr_generation_counter(void)
|
|
|
|
{
|
2008-10-28 22:21:40 +03:00
|
|
|
return atomic_long_read(&nfs_attr_generation_counter);
|
2008-10-15 03:16:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long nfs_inc_attr_generation_counter(void)
|
|
|
|
{
|
2008-10-28 22:21:40 +03:00
|
|
|
return atomic_long_inc_return(&nfs_attr_generation_counter);
|
2008-10-15 03:16:07 +04:00
|
|
|
}
|
2015-02-27 03:52:06 +03:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_inc_attr_generation_counter);
|
2008-10-15 03:16:07 +04:00
|
|
|
|
|
|
|
void nfs_fattr_init(struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
fattr->valid = 0;
|
|
|
|
fattr->time_start = jiffies;
|
|
|
|
fattr->gencount = nfs_inc_attr_generation_counter();
|
2012-01-07 22:22:46 +04:00
|
|
|
fattr->owner_name = NULL;
|
|
|
|
fattr->group_name = NULL;
|
2008-10-15 03:16:07 +04:00
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_fattr_init);
|
2008-10-15 03:16:07 +04:00
|
|
|
|
2015-02-27 01:42:42 +03:00
|
|
|
/**
|
|
|
|
* nfs_fattr_set_barrier
|
|
|
|
* @fattr: attributes
|
|
|
|
*
|
|
|
|
* Used to set a barrier after an attribute was updated. This
|
|
|
|
* barrier ensures that older attributes from RPC calls that may
|
|
|
|
* have raced with our update cannot clobber these new values.
|
|
|
|
* Note that you are still responsible for ensuring that other
|
|
|
|
* operations which change the attribute on the server do not
|
|
|
|
* collide.
|
|
|
|
*/
|
|
|
|
void nfs_fattr_set_barrier(struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
fattr->gencount = nfs_inc_attr_generation_counter();
|
|
|
|
}
|
|
|
|
|
2010-04-17 00:22:45 +04:00
|
|
|
struct nfs_fattr *nfs_alloc_fattr(void)
|
|
|
|
{
|
|
|
|
struct nfs_fattr *fattr;
|
|
|
|
|
|
|
|
fattr = kmalloc(sizeof(*fattr), GFP_NOFS);
|
|
|
|
if (fattr != NULL)
|
|
|
|
nfs_fattr_init(fattr);
|
|
|
|
return fattr;
|
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_alloc_fattr);
|
2010-04-17 00:22:45 +04:00
|
|
|
|
|
|
|
struct nfs_fh *nfs_alloc_fhandle(void)
|
|
|
|
{
|
|
|
|
struct nfs_fh *fh;
|
|
|
|
|
|
|
|
fh = kmalloc(sizeof(struct nfs_fh), GFP_NOFS);
|
|
|
|
if (fh != NULL)
|
|
|
|
fh->size = 0;
|
|
|
|
return fh;
|
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_alloc_fhandle);
|
2010-04-17 00:22:45 +04:00
|
|
|
|
2012-03-18 22:07:42 +04:00
|
|
|
#ifdef NFS_DEBUG
|
2012-03-07 05:46:43 +04:00
|
|
|
/*
|
|
|
|
* _nfs_display_fhandle_hash - calculate the crc32 hash for the filehandle
|
|
|
|
* in the same way that wireshark does
|
|
|
|
*
|
|
|
|
* @fh: file handle
|
|
|
|
*
|
|
|
|
* For debugging only.
|
|
|
|
*/
|
|
|
|
u32 _nfs_display_fhandle_hash(const struct nfs_fh *fh)
|
|
|
|
{
|
|
|
|
/* wireshark uses 32-bit AUTODIN crc and does a bitwise
|
|
|
|
* not on the result */
|
2013-08-13 00:06:31 +04:00
|
|
|
return nfs_fhandle_hash(fh);
|
2012-03-07 05:46:43 +04:00
|
|
|
}
|
2013-10-17 22:12:45 +04:00
|
|
|
EXPORT_SYMBOL_GPL(_nfs_display_fhandle_hash);
|
2012-03-07 05:46:43 +04:00
|
|
|
|
|
|
|
/*
|
2012-03-02 02:01:31 +04:00
|
|
|
* _nfs_display_fhandle - display an NFS file handle on the console
|
|
|
|
*
|
|
|
|
* @fh: file handle to display
|
|
|
|
* @caption: display caption
|
|
|
|
*
|
|
|
|
* For debugging only.
|
|
|
|
*/
|
|
|
|
void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption)
|
|
|
|
{
|
|
|
|
unsigned short i;
|
|
|
|
|
2012-03-06 19:14:35 +04:00
|
|
|
if (fh == NULL || fh->size == 0) {
|
2012-03-02 02:01:31 +04:00
|
|
|
printk(KERN_DEFAULT "%s at %p is empty\n", caption, fh);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-07 05:46:43 +04:00
|
|
|
printk(KERN_DEFAULT "%s at %p is %u bytes, crc: 0x%08x:\n",
|
|
|
|
caption, fh, fh->size, _nfs_display_fhandle_hash(fh));
|
2012-03-02 02:01:31 +04:00
|
|
|
for (i = 0; i < fh->size; i += 16) {
|
|
|
|
__be32 *pos = (__be32 *)&fh->data[i];
|
|
|
|
|
|
|
|
switch ((fh->size - i - 1) >> 2) {
|
|
|
|
case 0:
|
|
|
|
printk(KERN_DEFAULT " %08x\n",
|
|
|
|
be32_to_cpup(pos));
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
printk(KERN_DEFAULT " %08x %08x\n",
|
|
|
|
be32_to_cpup(pos), be32_to_cpup(pos + 1));
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
printk(KERN_DEFAULT " %08x %08x %08x\n",
|
|
|
|
be32_to_cpup(pos), be32_to_cpup(pos + 1),
|
|
|
|
be32_to_cpup(pos + 2));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk(KERN_DEFAULT " %08x %08x %08x %08x\n",
|
|
|
|
be32_to_cpup(pos), be32_to_cpup(pos + 1),
|
|
|
|
be32_to_cpup(pos + 2), be32_to_cpup(pos + 3));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-17 22:12:45 +04:00
|
|
|
EXPORT_SYMBOL_GPL(_nfs_display_fhandle);
|
2012-03-02 02:01:31 +04:00
|
|
|
#endif
|
|
|
|
|
2008-09-24 01:28:41 +04:00
|
|
|
/**
|
|
|
|
* nfs_inode_attrs_need_update - check if the inode attributes need updating
|
|
|
|
* @inode - pointer to inode
|
|
|
|
* @fattr - attributes
|
|
|
|
*
|
|
|
|
* Attempt to divine whether or not an RPC call reply carrying stale
|
|
|
|
* attributes got scheduled after another call carrying updated ones.
|
|
|
|
*
|
|
|
|
* To do so, the function first assumes that a more recent ctime means
|
|
|
|
* that the attributes in fattr are newer, however it also attempt to
|
|
|
|
* catch the case where ctime either didn't change, or went backwards
|
|
|
|
* (if someone reset the clock on the server) by looking at whether
|
|
|
|
* or not this RPC call was started after the inode was last updated.
|
2008-10-15 03:16:07 +04:00
|
|
|
* Note also the check for wraparound of 'attr_gencount'
|
2008-09-24 01:28:41 +04:00
|
|
|
*
|
|
|
|
* The function returns 'true' if it thinks the attributes in 'fattr' are
|
|
|
|
* more recent than the ones cached in the inode.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int nfs_inode_attrs_need_update(const struct inode *inode, const struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
const struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
2008-10-15 03:16:07 +04:00
|
|
|
return ((long)fattr->gencount - (long)nfsi->attr_gencount) > 0 ||
|
2008-10-09 21:27:55 +04:00
|
|
|
nfs_ctime_need_update(inode, fattr) ||
|
2008-10-15 03:16:07 +04:00
|
|
|
((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0);
|
2008-09-24 01:28:41 +04:00
|
|
|
}
|
|
|
|
|
2014-01-13 21:08:11 +04:00
|
|
|
/*
|
|
|
|
* Don't trust the change_attribute, mtime, ctime or size if
|
|
|
|
* a pnfs LAYOUTCOMMIT is outstanding
|
|
|
|
*/
|
|
|
|
static void nfs_inode_attrs_handle_layoutcommit(struct inode *inode,
|
|
|
|
struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
if (pnfs_layoutcommit_outstanding(inode))
|
|
|
|
fattr->valid &= ~(NFS_ATTR_FATTR_CHANGE |
|
|
|
|
NFS_ATTR_FATTR_MTIME |
|
|
|
|
NFS_ATTR_FATTR_CTIME |
|
|
|
|
NFS_ATTR_FATTR_SIZE);
|
|
|
|
}
|
|
|
|
|
2008-09-24 01:28:41 +04:00
|
|
|
static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
|
|
|
|
{
|
2013-08-20 02:59:33 +04:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
trace_nfs_refresh_inode_enter(inode);
|
|
|
|
|
2014-01-13 21:08:11 +04:00
|
|
|
nfs_inode_attrs_handle_layoutcommit(inode, fattr);
|
|
|
|
|
2008-09-24 01:28:41 +04:00
|
|
|
if (nfs_inode_attrs_need_update(inode, fattr))
|
2013-08-20 02:59:33 +04:00
|
|
|
ret = nfs_update_inode(inode, fattr);
|
|
|
|
else
|
|
|
|
ret = nfs_check_inode_attributes(inode, fattr);
|
|
|
|
|
|
|
|
trace_nfs_refresh_inode_exit(inode, ret);
|
|
|
|
return ret;
|
2008-10-05 20:07:23 +04:00
|
|
|
}
|
|
|
|
|
2005-10-28 06:12:39 +04:00
|
|
|
/**
|
|
|
|
* nfs_refresh_inode - try to update the inode attribute cache
|
|
|
|
* @inode - pointer to inode
|
|
|
|
* @fattr - updated attributes
|
|
|
|
*
|
|
|
|
* Check that an RPC call that returned attributes has not overlapped with
|
|
|
|
* other recent updates of the inode metadata, then decide whether it is
|
|
|
|
* safe to do a full update of the inode attributes, or whether just to
|
|
|
|
* call nfs_check_inode_attributes.
|
|
|
|
*/
|
|
|
|
int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
|
|
|
|
return 0;
|
|
|
|
spin_lock(&inode->i_lock);
|
2008-10-05 20:07:23 +04:00
|
|
|
status = nfs_refresh_inode_locked(inode, fattr);
|
2005-10-28 06:12:39 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
2009-04-03 19:42:43 +04:00
|
|
|
|
2005-10-28 06:12:39 +04:00
|
|
|
return status;
|
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_refresh_inode);
|
2005-10-28 06:12:39 +04:00
|
|
|
|
2008-10-05 20:27:55 +04:00
|
|
|
static int nfs_post_op_update_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
|
|
|
|
{
|
2014-06-20 21:11:01 +04:00
|
|
|
unsigned long invalid = NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
|
2008-10-05 20:27:55 +04:00
|
|
|
|
2014-06-20 21:11:01 +04:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
invalid |= NFS_INO_INVALID_DATA;
|
|
|
|
nfs_set_cache_invalid(inode, invalid);
|
2008-10-05 20:27:55 +04:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
|
|
|
|
return 0;
|
|
|
|
return nfs_refresh_inode_locked(inode, fattr);
|
|
|
|
}
|
|
|
|
|
2005-10-28 06:12:39 +04:00
|
|
|
/**
|
|
|
|
* nfs_post_op_update_inode - try to update the inode attribute cache
|
|
|
|
* @inode - pointer to inode
|
|
|
|
* @fattr - updated attributes
|
|
|
|
*
|
|
|
|
* After an operation that has changed the inode metadata, mark the
|
|
|
|
* attribute cache as being invalid, then try to update it.
|
2006-09-20 22:33:04 +04:00
|
|
|
*
|
|
|
|
* NB: if the server didn't return any post op attributes, this
|
|
|
|
* function will force the retrieval of attributes before the next
|
|
|
|
* NFS request. Thus it should be used only for operations that
|
|
|
|
* are expected to change one or more attributes, to avoid
|
|
|
|
* unnecessary NFS requests and trips through nfs_update_inode().
|
2005-10-28 06:12:39 +04:00
|
|
|
*/
|
|
|
|
int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
|
|
|
{
|
2008-10-05 20:27:55 +04:00
|
|
|
int status;
|
2005-10-28 06:12:39 +04:00
|
|
|
|
2007-10-01 17:59:15 +04:00
|
|
|
spin_lock(&inode->i_lock);
|
2015-02-27 03:48:26 +03:00
|
|
|
nfs_fattr_set_barrier(fattr);
|
2008-10-05 20:27:55 +04:00
|
|
|
status = nfs_post_op_update_inode_locked(inode, fattr);
|
2007-10-01 17:59:15 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
2013-05-22 20:50:44 +04:00
|
|
|
|
2008-10-05 20:07:23 +04:00
|
|
|
return status;
|
2005-10-28 06:12:39 +04:00
|
|
|
}
|
2012-07-31 00:05:24 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
|
2005-10-28 06:12:39 +04:00
|
|
|
|
2007-09-30 23:21:24 +04:00
|
|
|
/**
|
2015-02-27 01:36:09 +03:00
|
|
|
* nfs_post_op_update_inode_force_wcc_locked - update the inode attribute cache
|
2007-09-30 23:21:24 +04:00
|
|
|
* @inode - pointer to inode
|
|
|
|
* @fattr - updated attributes
|
|
|
|
*
|
|
|
|
* After an operation that has changed the inode metadata, mark the
|
|
|
|
* attribute cache as being invalid, then try to update it. Fake up
|
|
|
|
* weak cache consistency data, if none exist.
|
|
|
|
*
|
|
|
|
* This function is mainly designed to be used by the ->write_done() functions.
|
|
|
|
*/
|
2015-02-27 01:36:09 +03:00
|
|
|
int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr)
|
2007-09-30 23:21:24 +04:00
|
|
|
{
|
2008-10-05 20:27:55 +04:00
|
|
|
int status;
|
|
|
|
|
|
|
|
/* Don't do a WCC update if these attributes are already stale */
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR) == 0 ||
|
|
|
|
!nfs_inode_attrs_need_update(inode, fattr)) {
|
2009-03-11 21:10:24 +03:00
|
|
|
fattr->valid &= ~(NFS_ATTR_FATTR_PRECHANGE
|
|
|
|
| NFS_ATTR_FATTR_PRESIZE
|
|
|
|
| NFS_ATTR_FATTR_PREMTIME
|
|
|
|
| NFS_ATTR_FATTR_PRECTIME);
|
2008-10-05 20:27:55 +04:00
|
|
|
goto out_noforce;
|
|
|
|
}
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PRECHANGE) == 0) {
|
2011-10-18 03:08:46 +04:00
|
|
|
fattr->pre_change_attr = inode->i_version;
|
2009-03-11 21:10:24 +03:00
|
|
|
fattr->valid |= NFS_ATTR_FATTR_PRECHANGE;
|
2007-09-30 23:21:24 +04:00
|
|
|
}
|
2009-03-11 21:10:24 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CTIME) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PRECTIME) == 0) {
|
2007-09-30 23:21:24 +04:00
|
|
|
memcpy(&fattr->pre_ctime, &inode->i_ctime, sizeof(fattr->pre_ctime));
|
2009-03-11 21:10:24 +03:00
|
|
|
fattr->valid |= NFS_ATTR_FATTR_PRECTIME;
|
|
|
|
}
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PREMTIME) == 0) {
|
2007-09-30 23:21:24 +04:00
|
|
|
memcpy(&fattr->pre_mtime, &inode->i_mtime, sizeof(fattr->pre_mtime));
|
2009-03-11 21:10:24 +03:00
|
|
|
fattr->valid |= NFS_ATTR_FATTR_PREMTIME;
|
|
|
|
}
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_SIZE) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PRESIZE) == 0) {
|
2008-06-11 20:21:19 +04:00
|
|
|
fattr->pre_size = i_size_read(inode);
|
2009-03-11 21:10:24 +03:00
|
|
|
fattr->valid |= NFS_ATTR_FATTR_PRESIZE;
|
2007-09-30 23:21:24 +04:00
|
|
|
}
|
2008-10-05 20:27:55 +04:00
|
|
|
out_noforce:
|
|
|
|
status = nfs_post_op_update_inode_locked(inode, fattr);
|
2015-02-27 01:36:09 +03:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
|
|
|
|
* @inode - pointer to inode
|
|
|
|
* @fattr - updated attributes
|
|
|
|
*
|
|
|
|
* After an operation that has changed the inode metadata, mark the
|
|
|
|
* attribute cache as being invalid, then try to update it. Fake up
|
|
|
|
* weak cache consistency data, if none exist.
|
|
|
|
*
|
|
|
|
* This function is mainly designed to be used by the ->write_done() functions.
|
|
|
|
*/
|
|
|
|
int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
2015-02-27 01:54:58 +03:00
|
|
|
nfs_fattr_set_barrier(fattr);
|
2015-02-27 01:36:09 +03:00
|
|
|
status = nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
|
2008-10-05 20:27:55 +04:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
return status;
|
2007-09-30 23:21:24 +04:00
|
|
|
}
|
2012-07-31 00:05:23 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_post_op_update_inode_force_wcc);
|
2007-09-30 23:21:24 +04:00
|
|
|
|
nfs: Fetch MOUNTED_ON_FILEID when updating an inode
2ef47eb1 (NFS: Fix use of nfs_attr_use_mounted_on_fileid()) was a good
start to fixing a circular directory structure warning for NFS v4
"junctioned" mountpoints. Unfortunately, further testing continued to
generate this error.
My server is configured like this:
anna@nfsd ~ % df
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 9.1G 2.0G 6.5G 24% /
/dev/vdc1 1014M 33M 982M 4% /exports
/dev/vdc2 1014M 33M 982M 4% /exports/vol1
/dev/vdc3 1014M 33M 982M 4% /exports/vol1/vol2
anna@nfsd ~ % cat /etc/exports
/exports/ *(rw,async,no_subtree_check,no_root_squash)
/exports/vol1/ *(rw,async,no_subtree_check,no_root_squash)
/exports/vol1/vol2 *(rw,async,no_subtree_check,no_root_squash)
I've been running chown across the entire mountpoint twice in a row to
hit this problem. The first run succeeds, but the second one fails with
the circular directory warning along with:
anna@client ~ % dmesg
[Apr 3 14:28] NFS: server 192.168.100.204 error: fileid changed
fsid 0:39: expected fileid 0x100080, got 0x80
WHere 0x80 is the mountpoint's fileid and 0x100080 is the mounted-on
fileid.
This patch fixes the issue by requesting an updated mounted-on fileid
from the server during nfs_update_inode(), and then checking that the
fileid stored in the nfs_inode matches either the fileid or mounted-on
fileid returned by the server.
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2015-04-03 21:35:59 +03:00
|
|
|
|
|
|
|
static inline bool nfs_fileid_valid(struct nfs_inode *nfsi,
|
|
|
|
struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
bool ret1 = true, ret2 = true;
|
|
|
|
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_FILEID)
|
|
|
|
ret1 = (nfsi->fileid == fattr->fileid);
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
|
|
|
|
ret2 = (nfsi->fileid == fattr->mounted_on_fileid);
|
|
|
|
return ret1 || ret2;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Many nfs protocol calls return the new file attributes after
|
|
|
|
* an operation. Here we update the inode to reflect the state
|
|
|
|
* of the server's inode.
|
|
|
|
*
|
|
|
|
* This is a bit tricky because we have to make sure all dirty pages
|
|
|
|
* have been sent off to the server before calling invalidate_inode_pages.
|
|
|
|
* To make sure no other process adds more write requests while we try
|
|
|
|
* our best to flush them, we make them sleep during the attribute refresh.
|
|
|
|
*
|
|
|
|
* A very similar scenario holds for the dir cache.
|
|
|
|
*/
|
2005-12-03 23:20:07 +03:00
|
|
|
static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-06-09 17:34:19 +04:00
|
|
|
struct nfs_server *server;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2005-06-22 21:16:30 +04:00
|
|
|
loff_t cur_isize, new_isize;
|
2007-10-08 22:26:13 +04:00
|
|
|
unsigned long invalid = 0;
|
2007-02-06 01:26:28 +03:00
|
|
|
unsigned long now = jiffies;
|
2009-08-09 23:06:19 +04:00
|
|
|
unsigned long save_cache_validity;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-12-17 21:20:16 +04:00
|
|
|
dfprintk(VFS, "NFS: %s(%s/%lu fh_crc=0x%08x ct=%d info=0x%x)\n",
|
2008-05-03 00:42:44 +04:00
|
|
|
__func__, inode->i_sb->s_id, inode->i_ino,
|
2012-03-07 06:58:20 +04:00
|
|
|
nfs_display_fhandle_hash(NFS_FH(inode)),
|
2005-04-17 02:20:36 +04:00
|
|
|
atomic_read(&inode->i_count), fattr->valid);
|
|
|
|
|
nfs: Fetch MOUNTED_ON_FILEID when updating an inode
2ef47eb1 (NFS: Fix use of nfs_attr_use_mounted_on_fileid()) was a good
start to fixing a circular directory structure warning for NFS v4
"junctioned" mountpoints. Unfortunately, further testing continued to
generate this error.
My server is configured like this:
anna@nfsd ~ % df
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 9.1G 2.0G 6.5G 24% /
/dev/vdc1 1014M 33M 982M 4% /exports
/dev/vdc2 1014M 33M 982M 4% /exports/vol1
/dev/vdc3 1014M 33M 982M 4% /exports/vol1/vol2
anna@nfsd ~ % cat /etc/exports
/exports/ *(rw,async,no_subtree_check,no_root_squash)
/exports/vol1/ *(rw,async,no_subtree_check,no_root_squash)
/exports/vol1/vol2 *(rw,async,no_subtree_check,no_root_squash)
I've been running chown across the entire mountpoint twice in a row to
hit this problem. The first run succeeds, but the second one fails with
the circular directory warning along with:
anna@client ~ % dmesg
[Apr 3 14:28] NFS: server 192.168.100.204 error: fileid changed
fsid 0:39: expected fileid 0x100080, got 0x80
WHere 0x80 is the mountpoint's fileid and 0x100080 is the mounted-on
fileid.
This patch fixes the issue by requesting an updated mounted-on fileid
from the server during nfs_update_inode(), and then checking that the
fileid stored in the nfs_inode matches either the fileid or mounted-on
fileid returned by the server.
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
2015-04-03 21:35:59 +03:00
|
|
|
if (!nfs_fileid_valid(nfsi, fattr)) {
|
2012-04-30 19:32:57 +04:00
|
|
|
printk(KERN_ERR "NFS: server %s error: fileid changed\n"
|
|
|
|
"fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
|
|
|
|
NFS_SERVER(inode)->nfs_client->cl_hostname,
|
|
|
|
inode->i_sb->s_id, (long long)nfsi->fileid,
|
|
|
|
(long long)fattr->fileid);
|
|
|
|
goto out_err;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the inode's type hasn't changed.
|
|
|
|
*/
|
2012-04-30 19:32:57 +04:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
|
|
|
|
/*
|
|
|
|
* Big trouble! The inode has become a different object.
|
|
|
|
*/
|
2013-12-17 21:20:16 +04:00
|
|
|
printk(KERN_DEBUG "NFS: %s: inode %lu mode changed, %07o to %07o\n",
|
2012-04-30 19:32:57 +04:00
|
|
|
__func__, inode->i_ino, inode->i_mode, fattr->mode);
|
|
|
|
goto out_err;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-06-09 17:34:19 +04:00
|
|
|
server = NFS_SERVER(inode);
|
2007-06-05 21:26:15 +04:00
|
|
|
/* Update the fsid? */
|
2009-03-11 21:10:24 +03:00
|
|
|
if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) &&
|
2008-03-06 20:34:50 +03:00
|
|
|
!nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
|
2011-01-14 21:45:42 +03:00
|
|
|
!IS_AUTOMOUNT(inode))
|
2006-06-09 17:34:19 +04:00
|
|
|
server->fsid = fattr->fsid;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Update the read time so we don't revalidate too often.
|
|
|
|
*/
|
2005-10-28 06:12:39 +04:00
|
|
|
nfsi->read_cache_jiffies = fattr->time_start;
|
2007-02-06 01:26:28 +03:00
|
|
|
|
2009-08-09 23:06:19 +04:00
|
|
|
save_cache_validity = nfsi->cache_validity;
|
|
|
|
nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
|
|
|
|
| NFS_INO_INVALID_ATIME
|
|
|
|
| NFS_INO_REVAL_FORCED
|
|
|
|
| NFS_INO_REVAL_PAGECACHE);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-01-03 11:55:40 +03:00
|
|
|
/* Do atomic weak cache consistency updates */
|
2011-01-25 23:28:21 +03:00
|
|
|
invalid |= nfs_wcc_update_inode(inode, fattr);
|
2006-01-03 11:55:40 +03:00
|
|
|
|
2007-09-27 23:57:24 +04:00
|
|
|
/* More cache consistency checks */
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CHANGE) {
|
2011-10-18 03:08:46 +04:00
|
|
|
if (inode->i_version != fattr->change_attr) {
|
2009-03-11 21:10:24 +03:00
|
|
|
dprintk("NFS: change_attr change on server for file %s/%ld\n",
|
|
|
|
inode->i_sb->s_id, inode->i_ino);
|
2012-04-27 21:48:17 +04:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR
|
|
|
|
| NFS_INO_INVALID_DATA
|
|
|
|
| NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL
|
|
|
|
| NFS_INO_REVAL_PAGECACHE;
|
2009-03-11 21:10:24 +03:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
nfs_force_lookup_revalidate(inode);
|
2011-10-18 03:08:46 +04:00
|
|
|
inode->i_version = fattr->change_attr;
|
2009-03-11 21:10:24 +03:00
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
} else if (server->caps & NFS_CAP_CHANGE_ATTR)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity;
|
2009-03-11 21:10:24 +03:00
|
|
|
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
|
2012-04-27 21:48:18 +04:00
|
|
|
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
|
2009-08-09 23:06:19 +04:00
|
|
|
} else if (server->caps & NFS_CAP_MTIME)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_REVAL_FORCED);
|
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
|
2012-04-27 21:48:18 +04:00
|
|
|
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
|
2009-08-09 23:06:19 +04:00
|
|
|
} else if (server->caps & NFS_CAP_CTIME)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_REVAL_FORCED);
|
2007-09-27 23:57:24 +04:00
|
|
|
|
2005-06-22 21:16:30 +04:00
|
|
|
/* Check if our cached file size is stale */
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
|
|
|
|
new_isize = nfs_size_to_loff_t(fattr->size);
|
|
|
|
cur_isize = i_size_read(inode);
|
|
|
|
if (new_isize != cur_isize) {
|
|
|
|
/* Do we perhaps have any outstanding writes, or has
|
|
|
|
* the file grown beyond our last write? */
|
2014-11-12 20:08:00 +03:00
|
|
|
if ((nfsi->nrequests == 0) || new_isize > cur_isize) {
|
2009-03-11 21:10:24 +03:00
|
|
|
i_size_write(inode, new_isize);
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
|
2014-06-20 21:16:38 +04:00
|
|
|
invalid &= ~NFS_INO_REVAL_PAGECACHE;
|
2009-03-11 21:10:24 +03:00
|
|
|
}
|
2011-05-23 16:52:11 +04:00
|
|
|
dprintk("NFS: isize change on server for file %s/%ld "
|
|
|
|
"(%Ld to %Ld)\n",
|
|
|
|
inode->i_sb->s_id,
|
|
|
|
inode->i_ino,
|
|
|
|
(long long)cur_isize,
|
|
|
|
(long long)new_isize);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
} else
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_REVAL_PAGECACHE
|
|
|
|
| NFS_INO_REVAL_FORCED);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
|
|
|
|
memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
|
2009-08-09 23:06:19 +04:00
|
|
|
else if (server->caps & NFS_CAP_ATIME)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATIME
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_REVAL_FORCED);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MODE) {
|
|
|
|
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) {
|
2010-02-03 16:27:35 +03:00
|
|
|
umode_t newmode = inode->i_mode & S_IFMT;
|
|
|
|
newmode |= fattr->mode & S_IALLUGO;
|
|
|
|
inode->i_mode = newmode;
|
2009-03-11 21:10:24 +03:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
} else if (server->caps & NFS_CAP_MODE)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL
|
|
|
|
| NFS_INO_REVAL_FORCED);
|
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
|
2013-02-02 02:26:23 +04:00
|
|
|
if (!uid_eq(inode->i_uid, fattr->uid)) {
|
2009-03-11 21:10:24 +03:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
|
|
|
inode->i_uid = fattr->uid;
|
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
} else if (server->caps & NFS_CAP_OWNER)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL
|
|
|
|
| NFS_INO_REVAL_FORCED);
|
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
|
2013-02-02 02:26:23 +04:00
|
|
|
if (!gid_eq(inode->i_gid, fattr->gid)) {
|
2009-03-11 21:10:24 +03:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
|
|
|
inode->i_gid = fattr->gid;
|
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
} else if (server->caps & NFS_CAP_OWNER_GROUP)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL
|
|
|
|
| NFS_INO_REVAL_FORCED);
|
2008-10-15 03:23:07 +04:00
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
|
|
|
|
if (inode->i_nlink != fattr->nlink) {
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR;
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
invalid |= NFS_INO_INVALID_DATA;
|
2011-10-28 16:13:29 +04:00
|
|
|
set_nlink(inode, fattr->nlink);
|
2009-03-11 21:10:24 +03:00
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
} else if (server->caps & NFS_CAP_NLINK)
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_ATTR
|
2009-08-09 23:06:19 +04:00
|
|
|
| NFS_INO_REVAL_FORCED);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* report the blocks in 512byte units
|
|
|
|
*/
|
|
|
|
inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
|
|
|
|
}
|
2009-03-11 21:10:24 +03:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
|
|
|
|
inode->i_blocks = fattr->du.nfs2.blocks;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Update attrtimeo value if we're out of the unstable period */
|
2014-02-06 23:38:53 +04:00
|
|
|
if (invalid & NFS_INO_INVALID_ATTR) {
|
2006-03-20 21:44:14 +03:00
|
|
|
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
|
2005-04-17 02:20:36 +04:00
|
|
|
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
|
2007-02-06 01:26:28 +03:00
|
|
|
nfsi->attrtimeo_timestamp = now;
|
2015-02-27 03:34:32 +03:00
|
|
|
/* Set barrier to be more recent than all outstanding updates */
|
2008-10-15 03:16:07 +04:00
|
|
|
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
|
2007-10-02 02:57:50 +04:00
|
|
|
} else {
|
optimize attribute timeouts for "noac" and "actimeo=0"
Hi.
I've been looking at a bugzilla which describes a problem where
a customer was advised to use either the "noac" or "actimeo=0"
mount options to solve a consistency problem that they were
seeing in the file attributes. It turned out that this solution
did not work reliably for them because sometimes, the local
attribute cache was believed to be valid and not timed out.
(With an attribute cache timeout of 0, the cache should always
appear to be timed out.)
In looking at this situation, it appears to me that the problem
is that the attribute cache timeout code has an off-by-one
error in it. It is assuming that the cache is valid in the
region, [read_cache_jiffies, read_cache_jiffies + attrtimeo]. The
cache should be considered valid only in the region,
[read_cache_jiffies, read_cache_jiffies + attrtimeo). With this
change, the options, "noac" and "actimeo=0", work as originally
expected.
This problem was previously addressed by special casing the
attrtimeo == 0 case. However, since the problem is only an off-
by-one error, the cleaner solution is address the off-by-one
error and thus, not require the special case.
Thanx...
ps
Signed-off-by: Peter Staubach <staubach@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2008-12-23 23:21:56 +03:00
|
|
|
if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
|
2007-10-02 02:57:50 +04:00
|
|
|
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
|
|
|
|
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
|
|
|
|
nfsi->attrtimeo_timestamp = now;
|
|
|
|
}
|
2015-02-27 03:34:32 +03:00
|
|
|
/* Set the barrier to be more recent than this fattr */
|
|
|
|
if ((long)fattr->gencount - (long)nfsi->attr_gencount > 0)
|
|
|
|
nfsi->attr_gencount = fattr->gencount;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2007-08-15 20:49:17 +04:00
|
|
|
invalid &= ~NFS_INO_INVALID_ATTR;
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Don't invalidate the data if we were to blame */
|
|
|
|
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|
|
|
|
|| S_ISLNK(inode->i_mode)))
|
|
|
|
invalid &= ~NFS_INO_INVALID_DATA;
|
2012-06-20 23:53:43 +04:00
|
|
|
if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) ||
|
2009-08-09 23:06:19 +04:00
|
|
|
(save_cache_validity & NFS_INO_REVAL_FORCED))
|
2014-06-20 21:11:01 +04:00
|
|
|
nfs_set_cache_invalid(inode, invalid);
|
2012-12-21 01:52:38 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
2005-11-26 01:10:06 +03:00
|
|
|
out_err:
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* No need to worry about unhashing the dentry, as the
|
|
|
|
* lookup validation will know that the inode is bad.
|
|
|
|
* (But we fall through to invalidate the caches.)
|
|
|
|
*/
|
|
|
|
nfs_invalidate_inode(inode);
|
|
|
|
return -ESTALE;
|
|
|
|
}
|
|
|
|
|
NFS: Split fs/nfs/inode.c
As fs/nfs/inode.c is rather large, heterogenous and unwieldy, the attached
patch splits it up into a number of files:
(*) fs/nfs/inode.c
Strictly inode specific functions.
(*) fs/nfs/super.c
Superblock management functions for NFS and NFS4, normal access, clones
and referrals. The NFS4 superblock functions _could_ move out into a
separate conditionally compiled file, but it's probably not worth it as
there're so many common bits.
(*) fs/nfs/namespace.c
Some namespace-specific functions have been moved here.
(*) fs/nfs/nfs4namespace.c
NFS4-specific namespace functions (this could be merged into the previous
file). This file is conditionally compiled.
(*) fs/nfs/internal.h
Inter-file declarations, plus a few simple utility functions moved from
fs/nfs/inode.c.
Additionally, all the in-.c-file externs have been moved here, and those
files they were moved from now includes this file.
For the most part, the functions have not been changed, only some multiplexor
functions have changed significantly.
I've also:
(*) Added some extra banner comments above some functions.
(*) Rearranged the function order within the files to be more logical and
better grouped (IMO), though someone may prefer a different order.
(*) Reduced the number of #ifdefs in .c files.
(*) Added missing __init and __exit directives.
Signed-Off-By: David Howells <dhowells@redhat.com>
2006-06-09 17:34:33 +04:00
|
|
|
struct inode *nfs_alloc_inode(struct super_block *sb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi;
|
2015-04-23 12:17:51 +03:00
|
|
|
nfsi = kmem_cache_alloc(nfs_inode_cachep, GFP_KERNEL);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!nfsi)
|
|
|
|
return NULL;
|
2005-08-18 22:24:09 +04:00
|
|
|
nfsi->flags = 0UL;
|
|
|
|
nfsi->cache_validity = 0UL;
|
2012-07-31 00:05:25 +04:00
|
|
|
#if IS_ENABLED(CONFIG_NFS_V4)
|
2005-06-22 21:16:23 +04:00
|
|
|
nfsi->nfs4_acl = NULL;
|
|
|
|
#endif /* CONFIG_NFS_V4 */
|
2005-04-17 02:20:36 +04:00
|
|
|
return &nfsi->vfs_inode;
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_alloc_inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-01-07 09:49:49 +03:00
|
|
|
static void nfs_i_callback(struct rcu_head *head)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2011-01-07 09:49:49 +03:00
|
|
|
struct inode *inode = container_of(head, struct inode, i_rcu);
|
2005-04-17 02:20:36 +04:00
|
|
|
kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
|
|
|
|
}
|
|
|
|
|
2011-01-07 09:49:49 +03:00
|
|
|
void nfs_destroy_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
call_rcu(&inode->i_rcu, nfs_i_callback);
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_destroy_inode);
|
2011-01-07 09:49:49 +03:00
|
|
|
|
2006-06-25 13:41:26 +04:00
|
|
|
static inline void nfs4_init_once(struct nfs_inode *nfsi)
|
|
|
|
{
|
2012-07-31 00:05:25 +04:00
|
|
|
#if IS_ENABLED(CONFIG_NFS_V4)
|
2006-06-25 13:41:26 +04:00
|
|
|
INIT_LIST_HEAD(&nfsi->open_states);
|
|
|
|
nfsi->delegation = NULL;
|
|
|
|
init_rwsem(&nfsi->rwsem);
|
2010-10-20 08:18:01 +04:00
|
|
|
nfsi->layout = NULL;
|
2006-06-25 13:41:26 +04:00
|
|
|
#endif
|
|
|
|
}
|
NFS: Split fs/nfs/inode.c
As fs/nfs/inode.c is rather large, heterogenous and unwieldy, the attached
patch splits it up into a number of files:
(*) fs/nfs/inode.c
Strictly inode specific functions.
(*) fs/nfs/super.c
Superblock management functions for NFS and NFS4, normal access, clones
and referrals. The NFS4 superblock functions _could_ move out into a
separate conditionally compiled file, but it's probably not worth it as
there're so many common bits.
(*) fs/nfs/namespace.c
Some namespace-specific functions have been moved here.
(*) fs/nfs/nfs4namespace.c
NFS4-specific namespace functions (this could be merged into the previous
file). This file is conditionally compiled.
(*) fs/nfs/internal.h
Inter-file declarations, plus a few simple utility functions moved from
fs/nfs/inode.c.
Additionally, all the in-.c-file externs have been moved here, and those
files they were moved from now includes this file.
For the most part, the functions have not been changed, only some multiplexor
functions have changed significantly.
I've also:
(*) Added some extra banner comments above some functions.
(*) Rearranged the function order within the files to be more logical and
better grouped (IMO), though someone may prefer a different order.
(*) Reduced the number of #ifdefs in .c files.
(*) Added missing __init and __exit directives.
Signed-Off-By: David Howells <dhowells@redhat.com>
2006-06-09 17:34:33 +04:00
|
|
|
|
2008-07-26 06:45:34 +04:00
|
|
|
static void init_once(void *foo)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = (struct nfs_inode *) foo;
|
|
|
|
|
2007-05-17 09:10:57 +04:00
|
|
|
inode_init_once(&nfsi->vfs_inode);
|
|
|
|
INIT_LIST_HEAD(&nfsi->open_files);
|
|
|
|
INIT_LIST_HEAD(&nfsi->access_cache_entry_lru);
|
|
|
|
INIT_LIST_HEAD(&nfsi->access_cache_inode_lru);
|
2012-04-20 22:47:53 +04:00
|
|
|
INIT_LIST_HEAD(&nfsi->commit_info.list);
|
2014-11-12 20:08:00 +03:00
|
|
|
nfsi->nrequests = 0;
|
2012-04-20 22:47:53 +04:00
|
|
|
nfsi->commit_info.ncommit = 0;
|
2012-06-20 02:38:56 +04:00
|
|
|
atomic_set(&nfsi->commit_info.rpcs_out, 0);
|
2007-10-16 02:17:53 +04:00
|
|
|
atomic_set(&nfsi->silly_count, 1);
|
|
|
|
INIT_HLIST_HEAD(&nfsi->silly_list);
|
|
|
|
init_waitqueue_head(&nfsi->waitqueue);
|
2007-05-17 09:10:57 +04:00
|
|
|
nfs4_init_once(nfsi);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2007-07-20 05:11:58 +04:00
|
|
|
|
NFS: Split fs/nfs/inode.c
As fs/nfs/inode.c is rather large, heterogenous and unwieldy, the attached
patch splits it up into a number of files:
(*) fs/nfs/inode.c
Strictly inode specific functions.
(*) fs/nfs/super.c
Superblock management functions for NFS and NFS4, normal access, clones
and referrals. The NFS4 superblock functions _could_ move out into a
separate conditionally compiled file, but it's probably not worth it as
there're so many common bits.
(*) fs/nfs/namespace.c
Some namespace-specific functions have been moved here.
(*) fs/nfs/nfs4namespace.c
NFS4-specific namespace functions (this could be merged into the previous
file). This file is conditionally compiled.
(*) fs/nfs/internal.h
Inter-file declarations, plus a few simple utility functions moved from
fs/nfs/inode.c.
Additionally, all the in-.c-file externs have been moved here, and those
files they were moved from now includes this file.
For the most part, the functions have not been changed, only some multiplexor
functions have changed significantly.
I've also:
(*) Added some extra banner comments above some functions.
(*) Rearranged the function order within the files to be more logical and
better grouped (IMO), though someone may prefer a different order.
(*) Reduced the number of #ifdefs in .c files.
(*) Added missing __init and __exit directives.
Signed-Off-By: David Howells <dhowells@redhat.com>
2006-06-09 17:34:33 +04:00
|
|
|
static int __init nfs_init_inodecache(void)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
|
|
|
|
sizeof(struct nfs_inode),
|
2006-03-24 14:16:06 +03:00
|
|
|
0, (SLAB_RECLAIM_ACCOUNT|
|
|
|
|
SLAB_MEM_SPREAD),
|
2007-07-20 05:11:58 +04:00
|
|
|
init_once);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (nfs_inode_cachep == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-27 23:59:15 +04:00
|
|
|
static void nfs_destroy_inodecache(void)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2012-09-26 05:33:07 +04:00
|
|
|
/*
|
|
|
|
* Make sure all delayed rcu free inodes are flushed before we
|
|
|
|
* destroy cache.
|
|
|
|
*/
|
|
|
|
rcu_barrier();
|
2006-09-27 12:49:40 +04:00
|
|
|
kmem_cache_destroy(nfs_inode_cachep);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2008-02-20 04:04:22 +03:00
|
|
|
struct workqueue_struct *nfsiod_workqueue;
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfsiod_workqueue);
|
2008-02-20 04:04:22 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* start up the nfsiod workqueue
|
|
|
|
*/
|
|
|
|
static int nfsiod_start(void)
|
|
|
|
{
|
|
|
|
struct workqueue_struct *wq;
|
|
|
|
dprintk("RPC: creating workqueue nfsiod\n");
|
2011-01-25 16:35:54 +03:00
|
|
|
wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM, 0);
|
2008-02-20 04:04:22 +03:00
|
|
|
if (wq == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
nfsiod_workqueue = wq;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destroy the nfsiod workqueue
|
|
|
|
*/
|
|
|
|
static void nfsiod_stop(void)
|
|
|
|
{
|
|
|
|
struct workqueue_struct *wq;
|
|
|
|
|
|
|
|
wq = nfsiod_workqueue;
|
|
|
|
if (wq == NULL)
|
|
|
|
return;
|
|
|
|
nfsiod_workqueue = NULL;
|
|
|
|
destroy_workqueue(wq);
|
|
|
|
}
|
|
|
|
|
2011-11-25 18:13:04 +04:00
|
|
|
int nfs_net_id;
|
2012-01-10 17:04:24 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_net_id);
|
2011-11-25 18:13:04 +04:00
|
|
|
|
|
|
|
static int nfs_net_init(struct net *net)
|
|
|
|
{
|
2012-01-23 21:26:05 +04:00
|
|
|
nfs_clients_init(net);
|
2014-07-31 15:35:20 +04:00
|
|
|
return nfs_fs_proc_net_init(net);
|
2011-11-25 18:13:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs_net_exit(struct net *net)
|
|
|
|
{
|
2014-07-31 15:35:20 +04:00
|
|
|
nfs_fs_proc_net_exit(net);
|
2012-01-23 21:26:22 +04:00
|
|
|
nfs_cleanup_cb_ident_idr(net);
|
2011-11-25 18:13:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations nfs_net_ops = {
|
|
|
|
.init = nfs_net_init,
|
|
|
|
.exit = nfs_net_exit,
|
|
|
|
.id = &nfs_net_id,
|
|
|
|
.size = sizeof(struct nfs_net),
|
|
|
|
};
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Initialize NFS
|
|
|
|
*/
|
|
|
|
static int __init init_nfs_fs(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2011-11-25 18:13:04 +04:00
|
|
|
err = register_pernet_subsys(&nfs_net_ops);
|
2009-08-20 02:12:27 +04:00
|
|
|
if (err < 0)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out9;
|
2009-08-20 02:12:27 +04:00
|
|
|
|
2009-04-03 19:42:42 +04:00
|
|
|
err = nfs_fscache_register();
|
|
|
|
if (err < 0)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out8;
|
2009-04-03 19:42:42 +04:00
|
|
|
|
2008-02-20 04:04:22 +03:00
|
|
|
err = nfsiod_start();
|
|
|
|
if (err)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out7;
|
2008-02-20 04:04:22 +03:00
|
|
|
|
2006-08-23 04:06:13 +04:00
|
|
|
err = nfs_fs_proc_init();
|
|
|
|
if (err)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out6;
|
2006-08-23 04:06:13 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
err = nfs_init_nfspagecache();
|
|
|
|
if (err)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out5;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
err = nfs_init_inodecache();
|
|
|
|
if (err)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out4;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
err = nfs_init_readpagecache();
|
|
|
|
if (err)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out3;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
err = nfs_init_writepagecache();
|
|
|
|
if (err)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out2;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
err = nfs_init_directcache();
|
|
|
|
if (err)
|
2012-07-31 00:05:25 +04:00
|
|
|
goto out1;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2011-12-06 17:42:40 +04:00
|
|
|
rpc_proc_register(&init_net, &nfs_rpcstat);
|
2005-04-17 02:20:36 +04:00
|
|
|
#endif
|
NFS: Split fs/nfs/inode.c
As fs/nfs/inode.c is rather large, heterogenous and unwieldy, the attached
patch splits it up into a number of files:
(*) fs/nfs/inode.c
Strictly inode specific functions.
(*) fs/nfs/super.c
Superblock management functions for NFS and NFS4, normal access, clones
and referrals. The NFS4 superblock functions _could_ move out into a
separate conditionally compiled file, but it's probably not worth it as
there're so many common bits.
(*) fs/nfs/namespace.c
Some namespace-specific functions have been moved here.
(*) fs/nfs/nfs4namespace.c
NFS4-specific namespace functions (this could be merged into the previous
file). This file is conditionally compiled.
(*) fs/nfs/internal.h
Inter-file declarations, plus a few simple utility functions moved from
fs/nfs/inode.c.
Additionally, all the in-.c-file externs have been moved here, and those
files they were moved from now includes this file.
For the most part, the functions have not been changed, only some multiplexor
functions have changed significantly.
I've also:
(*) Added some extra banner comments above some functions.
(*) Rearranged the function order within the files to be more logical and
better grouped (IMO), though someone may prefer a different order.
(*) Reduced the number of #ifdefs in .c files.
(*) Added missing __init and __exit directives.
Signed-Off-By: David Howells <dhowells@redhat.com>
2006-06-09 17:34:33 +04:00
|
|
|
if ((err = register_nfs_fs()) != 0)
|
2012-07-17 00:39:13 +04:00
|
|
|
goto out0;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
2012-07-17 00:39:13 +04:00
|
|
|
out0:
|
2005-04-17 02:20:36 +04:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2011-12-06 17:42:40 +04:00
|
|
|
rpc_proc_unregister(&init_net, "nfs");
|
2005-04-17 02:20:36 +04:00
|
|
|
#endif
|
|
|
|
nfs_destroy_directcache();
|
2012-07-31 00:05:25 +04:00
|
|
|
out1:
|
2012-07-17 00:39:13 +04:00
|
|
|
nfs_destroy_writepagecache();
|
2012-07-31 00:05:25 +04:00
|
|
|
out2:
|
2012-07-17 00:39:13 +04:00
|
|
|
nfs_destroy_readpagecache();
|
2012-07-31 00:05:25 +04:00
|
|
|
out3:
|
2012-07-17 00:39:13 +04:00
|
|
|
nfs_destroy_inodecache();
|
2012-07-31 00:05:25 +04:00
|
|
|
out4:
|
2012-07-17 00:39:13 +04:00
|
|
|
nfs_destroy_nfspagecache();
|
2012-07-31 00:05:25 +04:00
|
|
|
out5:
|
2012-07-17 00:39:13 +04:00
|
|
|
nfs_fs_proc_exit();
|
2012-07-31 00:05:25 +04:00
|
|
|
out6:
|
2012-07-17 00:39:13 +04:00
|
|
|
nfsiod_stop();
|
2012-07-31 00:05:25 +04:00
|
|
|
out7:
|
2012-07-17 00:39:13 +04:00
|
|
|
nfs_fscache_unregister();
|
2012-07-31 00:05:25 +04:00
|
|
|
out8:
|
2012-07-17 00:39:13 +04:00
|
|
|
unregister_pernet_subsys(&nfs_net_ops);
|
2012-07-31 00:05:25 +04:00
|
|
|
out9:
|
2005-04-17 02:20:36 +04:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit exit_nfs_fs(void)
|
|
|
|
{
|
|
|
|
nfs_destroy_directcache();
|
|
|
|
nfs_destroy_writepagecache();
|
|
|
|
nfs_destroy_readpagecache();
|
|
|
|
nfs_destroy_inodecache();
|
|
|
|
nfs_destroy_nfspagecache();
|
2009-04-03 19:42:42 +04:00
|
|
|
nfs_fscache_unregister();
|
2011-11-25 18:13:04 +04:00
|
|
|
unregister_pernet_subsys(&nfs_net_ops);
|
2005-04-17 02:20:36 +04:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2011-12-06 17:42:40 +04:00
|
|
|
rpc_proc_unregister(&init_net, "nfs");
|
2005-04-17 02:20:36 +04:00
|
|
|
#endif
|
NFS: Split fs/nfs/inode.c
As fs/nfs/inode.c is rather large, heterogenous and unwieldy, the attached
patch splits it up into a number of files:
(*) fs/nfs/inode.c
Strictly inode specific functions.
(*) fs/nfs/super.c
Superblock management functions for NFS and NFS4, normal access, clones
and referrals. The NFS4 superblock functions _could_ move out into a
separate conditionally compiled file, but it's probably not worth it as
there're so many common bits.
(*) fs/nfs/namespace.c
Some namespace-specific functions have been moved here.
(*) fs/nfs/nfs4namespace.c
NFS4-specific namespace functions (this could be merged into the previous
file). This file is conditionally compiled.
(*) fs/nfs/internal.h
Inter-file declarations, plus a few simple utility functions moved from
fs/nfs/inode.c.
Additionally, all the in-.c-file externs have been moved here, and those
files they were moved from now includes this file.
For the most part, the functions have not been changed, only some multiplexor
functions have changed significantly.
I've also:
(*) Added some extra banner comments above some functions.
(*) Rearranged the function order within the files to be more logical and
better grouped (IMO), though someone may prefer a different order.
(*) Reduced the number of #ifdefs in .c files.
(*) Added missing __init and __exit directives.
Signed-Off-By: David Howells <dhowells@redhat.com>
2006-06-09 17:34:33 +04:00
|
|
|
unregister_nfs_fs();
|
2006-08-23 04:06:13 +04:00
|
|
|
nfs_fs_proc_exit();
|
2008-02-20 04:04:22 +03:00
|
|
|
nfsiod_stop();
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Not quite true; I just maintain it */
|
|
|
|
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
|
|
|
|
MODULE_LICENSE("GPL");
|
2007-10-09 20:01:04 +04:00
|
|
|
module_param(enable_ino64, bool, 0644);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
module_init(init_nfs_fs)
|
|
|
|
module_exit(exit_nfs_fs)
|