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>
|
2017-02-02 21:15:33 +03:00
|
|
|
#include <linux/sched/signal.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
|
|
|
|
2016-12-24 22:46:01 +03:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-01-06 18:40:18 +03:00
|
|
|
static int nfs_wait_killable(int mode)
|
2009-03-11 21:10:30 +03:00
|
|
|
{
|
2013-05-07 03:50:06 +04:00
|
|
|
freezable_schedule_unsafe();
|
2015-12-14 00:11:16 +03:00
|
|
|
if (signal_pending_state(mode, current))
|
|
|
|
return -ERESTARTSYS;
|
2009-03-11 21:10:30 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2016-01-06 18:40:18 +03:00
|
|
|
|
|
|
|
int nfs_wait_bit_killable(struct wait_bit_key *key, int mode)
|
|
|
|
{
|
|
|
|
return nfs_wait_killable(mode);
|
|
|
|
}
|
2012-07-31 00:05:25 +04:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_wait_bit_killable);
|
2009-03-11 21:10:30 +03:00
|
|
|
|
2016-01-06 18:40:18 +03:00
|
|
|
int nfs_wait_atomic_killable(atomic_t *p)
|
|
|
|
{
|
|
|
|
return nfs_wait_killable(TASK_KILLABLE);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2016-03-02 19:35:55 +03:00
|
|
|
inode_dio_wait(inode);
|
2015-03-25 23:38:33 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-12-17 02:51:15 +03:00
|
|
|
static int nfs_attribute_timeout(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
|
|
|
return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
|
|
|
|
}
|
|
|
|
|
2016-12-09 02:18:38 +03:00
|
|
|
static bool nfs_check_cache_invalid_delegated(struct inode *inode, unsigned long flags)
|
|
|
|
{
|
|
|
|
unsigned long cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
|
|
|
|
|
|
|
|
/* Special case for the pagecache or access cache */
|
|
|
|
if (flags == NFS_INO_REVAL_PAGECACHE &&
|
|
|
|
!(cache_validity & NFS_INO_REVAL_FORCED))
|
|
|
|
return false;
|
|
|
|
return (cache_validity & flags) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nfs_check_cache_invalid_not_delegated(struct inode *inode, unsigned long flags)
|
|
|
|
{
|
|
|
|
unsigned long cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
|
|
|
|
|
|
|
|
if ((cache_validity & flags) != 0)
|
|
|
|
return true;
|
|
|
|
if (nfs_attribute_timeout(inode))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nfs_check_cache_invalid(struct inode *inode, unsigned long flags)
|
|
|
|
{
|
|
|
|
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
|
|
|
|
return nfs_check_cache_invalid_delegated(inode, flags);
|
|
|
|
|
|
|
|
return nfs_check_cache_invalid_not_delegated(inode, flags);
|
|
|
|
}
|
|
|
|
|
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);
|
2016-06-17 23:48:28 +03:00
|
|
|
inode->i_mode = fattr->mode;
|
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
|
|
|
}
|
2015-11-17 09:07:57 +03:00
|
|
|
} else if (S_ISLNK(inode->i_mode)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_op = &nfs_symlink_inode_operations;
|
2015-11-17 09:07:57 +03:00
|
|
|
inode_nohighmem(inode);
|
|
|
|
} else
|
2005-04-17 02:20:36 +04:00
|
|
|
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;
|
2015-07-05 18:12:07 +03:00
|
|
|
else
|
2015-07-05 23:07:04 +03: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_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;
|
2015-08-26 16:10:55 +03:00
|
|
|
int error = 0;
|
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) {
|
2014-09-07 19:36:40 +04:00
|
|
|
BUG_ON(!S_ISREG(inode->i_mode));
|
|
|
|
|
2015-08-26 16:10:55 +03:00
|
|
|
error = inode_newsize_ok(inode, attr->ia_size);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (attr->ia_size == i_size_read(inode))
|
2005-04-17 02:20:36 +04:00
|
|
|
attr->ia_valid &= ~ATTR_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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();
|
2015-08-26 16:10:55 +03:00
|
|
|
if (fattr == NULL) {
|
|
|
|
error = -ENOMEM;
|
2010-04-17 00:22:52 +04:00
|
|
|
goto out;
|
2015-08-26 16:10:55 +03:00
|
|
|
}
|
|
|
|
|
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-11-25 21:50:45 +03:00
|
|
|
if (fattr->valid)
|
|
|
|
nfs_update_inode(inode, fattr);
|
|
|
|
else
|
|
|
|
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR;
|
2015-02-27 00:09:04 +03:00
|
|
|
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
|
|
|
|
2016-12-02 17:15:37 +03:00
|
|
|
static void nfs_readdirplus_parent_cache_miss(struct dentry *dentry)
|
2014-02-08 02:02:08 +04:00
|
|
|
{
|
|
|
|
struct dentry *parent;
|
|
|
|
|
2016-12-02 17:15:37 +03:00
|
|
|
if (!nfs_server_capable(d_inode(dentry), NFS_CAP_READDIRPLUS))
|
|
|
|
return;
|
2014-02-08 02:02:08 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-12-02 17:15:37 +03:00
|
|
|
static void nfs_readdirplus_parent_cache_hit(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct dentry *parent;
|
|
|
|
|
|
|
|
if (!nfs_server_capable(d_inode(dentry), NFS_CAP_READDIRPLUS))
|
|
|
|
return;
|
|
|
|
parent = dget_parent(dentry);
|
|
|
|
nfs_advise_use_readdirplus(d_inode(parent));
|
|
|
|
dput(parent);
|
|
|
|
}
|
|
|
|
|
2014-02-08 02:02:08 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-01-31 19:46:22 +03:00
|
|
|
int nfs_getattr(const struct path *path, struct kstat *stat,
|
|
|
|
u32 request_mask, unsigned int query_flags)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-01-31 19:46:22 +03:00
|
|
|
struct inode *inode = d_inode(path->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)) {
|
2016-06-26 00:45:40 +03:00
|
|
|
err = filemap_write_and_wait(inode->i_mapping);
|
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.
|
|
|
|
*/
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-01-31 19:46:22 +03:00
|
|
|
if ((path->mnt->mnt_flags & MNT_NOATIME) ||
|
|
|
|
((path->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);
|
|
|
|
|
2017-04-28 13:35:19 +03:00
|
|
|
if (!(server->flags & NFS_MOUNT_NOAC))
|
|
|
|
nfs_readdirplus_parent_cache_miss(path->dentry);
|
|
|
|
else
|
|
|
|
nfs_readdirplus_parent_cache_hit(path->dentry);
|
2014-02-08 02:02:08 +04:00
|
|
|
err = __nfs_revalidate_inode(server, inode);
|
2016-12-02 17:15:37 +03:00
|
|
|
} else
|
statx: Add a system call to make enhanced file info available
Add a system call to make extended file information available, including
file creation and some attribute flags where available through the
underlying filesystem.
The getattr inode operation is altered to take two additional arguments: a
u32 request_mask and an unsigned int flags that indicate the
synchronisation mode. This change is propagated to the vfs_getattr*()
function.
Functions like vfs_stat() are now inline wrappers around new functions
vfs_statx() and vfs_statx_fd() to reduce stack usage.
========
OVERVIEW
========
The idea was initially proposed as a set of xattrs that could be retrieved
with getxattr(), but the general preference proved to be for a new syscall
with an extended stat structure.
A number of requests were gathered for features to be included. The
following have been included:
(1) Make the fields a consistent size on all arches and make them large.
(2) Spare space, request flags and information flags are provided for
future expansion.
(3) Better support for the y2038 problem [Arnd Bergmann] (tv_sec is an
__s64).
(4) Creation time: The SMB protocol carries the creation time, which could
be exported by Samba, which will in turn help CIFS make use of
FS-Cache as that can be used for coherency data (stx_btime).
This is also specified in NFSv4 as a recommended attribute and could
be exported by NFSD [Steve French].
(5) Lightweight stat: Ask for just those details of interest, and allow a
netfs (such as NFS) to approximate anything not of interest, possibly
without going to the server [Trond Myklebust, Ulrich Drepper, Andreas
Dilger] (AT_STATX_DONT_SYNC).
(6) Heavyweight stat: Force a netfs to go to the server, even if it thinks
its cached attributes are up to date [Trond Myklebust]
(AT_STATX_FORCE_SYNC).
And the following have been left out for future extension:
(7) Data version number: Could be used by userspace NFS servers [Aneesh
Kumar].
Can also be used to modify fill_post_wcc() in NFSD which retrieves
i_version directly, but has just called vfs_getattr(). It could get
it from the kstat struct if it used vfs_xgetattr() instead.
(There's disagreement on the exact semantics of a single field, since
not all filesystems do this the same way).
(8) BSD stat compatibility: Including more fields from the BSD stat such
as creation time (st_btime) and inode generation number (st_gen)
[Jeremy Allison, Bernd Schubert].
(9) Inode generation number: Useful for FUSE and userspace NFS servers
[Bernd Schubert].
(This was asked for but later deemed unnecessary with the
open-by-handle capability available and caused disagreement as to
whether it's a security hole or not).
(10) Extra coherency data may be useful in making backups [Andreas Dilger].
(No particular data were offered, but things like last backup
timestamp, the data version number and the DOS archive bit would come
into this category).
(11) Allow the filesystem to indicate what it can/cannot provide: A
filesystem can now say it doesn't support a standard stat feature if
that isn't available, so if, for instance, inode numbers or UIDs don't
exist or are fabricated locally...
(This requires a separate system call - I have an fsinfo() call idea
for this).
(12) Store a 16-byte volume ID in the superblock that can be returned in
struct xstat [Steve French].
(Deferred to fsinfo).
(13) Include granularity fields in the time data to indicate the
granularity of each of the times (NFSv4 time_delta) [Steve French].
(Deferred to fsinfo).
(14) FS_IOC_GETFLAGS value. These could be translated to BSD's st_flags.
Note that the Linux IOC flags are a mess and filesystems such as Ext4
define flags that aren't in linux/fs.h, so translation in the kernel
may be a necessity (or, possibly, we provide the filesystem type too).
(Some attributes are made available in stx_attributes, but the general
feeling was that the IOC flags were to ext[234]-specific and shouldn't
be exposed through statx this way).
(15) Mask of features available on file (eg: ACLs, seclabel) [Brad Boyer,
Michael Kerrisk].
(Deferred, probably to fsinfo. Finding out if there's an ACL or
seclabal might require extra filesystem operations).
(16) Femtosecond-resolution timestamps [Dave Chinner].
(A __reserved field has been left in the statx_timestamp struct for
this - if there proves to be a need).
(17) A set multiple attributes syscall to go with this.
===============
NEW SYSTEM CALL
===============
The new system call is:
int ret = statx(int dfd,
const char *filename,
unsigned int flags,
unsigned int mask,
struct statx *buffer);
The dfd, filename and flags parameters indicate the file to query, in a
similar way to fstatat(). There is no equivalent of lstat() as that can be
emulated with statx() by passing AT_SYMLINK_NOFOLLOW in flags. There is
also no equivalent of fstat() as that can be emulated by passing a NULL
filename to statx() with the fd of interest in dfd.
Whether or not statx() synchronises the attributes with the backing store
can be controlled by OR'ing a value into the flags argument (this typically
only affects network filesystems):
(1) AT_STATX_SYNC_AS_STAT tells statx() to behave as stat() does in this
respect.
(2) AT_STATX_FORCE_SYNC will require a network filesystem to synchronise
its attributes with the server - which might require data writeback to
occur to get the timestamps correct.
(3) AT_STATX_DONT_SYNC will suppress synchronisation with the server in a
network filesystem. The resulting values should be considered
approximate.
mask is a bitmask indicating the fields in struct statx that are of
interest to the caller. The user should set this to STATX_BASIC_STATS to
get the basic set returned by stat(). It should be noted that asking for
more information may entail extra I/O operations.
buffer points to the destination for the data. This must be 256 bytes in
size.
======================
MAIN ATTRIBUTES RECORD
======================
The following structures are defined in which to return the main attribute
set:
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 __spare1[1];
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
The defined bits in request_mask and stx_mask are:
STATX_TYPE Want/got stx_mode & S_IFMT
STATX_MODE Want/got stx_mode & ~S_IFMT
STATX_NLINK Want/got stx_nlink
STATX_UID Want/got stx_uid
STATX_GID Want/got stx_gid
STATX_ATIME Want/got stx_atime{,_ns}
STATX_MTIME Want/got stx_mtime{,_ns}
STATX_CTIME Want/got stx_ctime{,_ns}
STATX_INO Want/got stx_ino
STATX_SIZE Want/got stx_size
STATX_BLOCKS Want/got stx_blocks
STATX_BASIC_STATS [The stuff in the normal stat struct]
STATX_BTIME Want/got stx_btime{,_ns}
STATX_ALL [All currently available stuff]
stx_btime is the file creation time, stx_mask is a bitmask indicating the
data provided and __spares*[] are where as-yet undefined fields can be
placed.
Time fields are structures with separate seconds and nanoseconds fields
plus a reserved field in case we want to add even finer resolution. Note
that times will be negative if before 1970; in such a case, the nanosecond
fields will also be negative if not zero.
The bits defined in the stx_attributes field convey information about a
file, how it is accessed, where it is and what it does. The following
attributes map to FS_*_FL flags and are the same numerical value:
STATX_ATTR_COMPRESSED File is compressed by the fs
STATX_ATTR_IMMUTABLE File is marked immutable
STATX_ATTR_APPEND File is append-only
STATX_ATTR_NODUMP File is not to be dumped
STATX_ATTR_ENCRYPTED File requires key to decrypt in fs
Within the kernel, the supported flags are listed by:
KSTAT_ATTR_FS_IOC_FLAGS
[Are any other IOC flags of sufficient general interest to be exposed
through this interface?]
New flags include:
STATX_ATTR_AUTOMOUNT Object is an automount trigger
These are for the use of GUI tools that might want to mark files specially,
depending on what they are.
Fields in struct statx come in a number of classes:
(0) stx_dev_*, stx_blksize.
These are local system information and are always available.
(1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time, stx_ino,
stx_size, stx_blocks.
These will be returned whether the caller asks for them or not. The
corresponding bits in stx_mask will be set to indicate whether they
actually have valid values.
If the caller didn't ask for them, then they may be approximated. For
example, NFS won't waste any time updating them from the server,
unless as a byproduct of updating something requested.
If the values don't actually exist for the underlying object (such as
UID or GID on a DOS file), then the bit won't be set in the stx_mask,
even if the caller asked for the value. In such a case, the returned
value will be a fabrication.
Note that there are instances where the type might not be valid, for
instance Windows reparse points.
(2) stx_rdev_*.
This will be set only if stx_mode indicates we're looking at a
blockdev or a chardev, otherwise will be 0.
(3) stx_btime.
Similar to (1), except this will be set to 0 if it doesn't exist.
=======
TESTING
=======
The following test program can be used to test the statx system call:
samples/statx/test-statx.c
Just compile and run, passing it paths to the files you want to examine.
The file is built automatically if CONFIG_SAMPLES is enabled.
Here's some example output. Firstly, an NFS directory that crosses to
another FSID. Note that the AUTOMOUNT attribute is set because transiting
this directory will cause d_automount to be invoked by the VFS.
[root@andromeda ~]# /tmp/test-statx -A /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:26 Inode: 1703937 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Attributes: 0000000000001000 (-------- -------- -------- -------- -------- -------- ---m---- --------)
Secondly, the result of automounting on that directory.
[root@andromeda ~]# /tmp/test-statx /warthog/data
statx(/warthog/data) = 0
results=7ff
Size: 4096 Blocks: 8 IO Block: 1048576 directory
Device: 00:27 Inode: 2 Links: 125
Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041
Access: 2016-11-24 09:02:12.219699527+0000
Modify: 2016-11-17 10:44:36.225653653+0000
Change: 2016-11-17 10:44:36.225653653+0000
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2017-01-31 19:46:22 +03:00
|
|
|
nfs_readdirplus_parent_cache_hit(path->dentry);
|
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));
|
2015-05-08 06:10:40 +03:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
stat->blksize = NFS_SERVER(inode)->dtsize;
|
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);
|
2016-10-13 07:26:47 +03:00
|
|
|
l_ctx->lockowner = current->files;
|
2010-06-26 00:35:53 +04:00
|
|
|
INIT_LIST_HEAD(&l_ctx->list);
|
2016-01-06 18:40:18 +03:00
|
|
|
atomic_set(&l_ctx->io_count, 0);
|
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 {
|
2016-10-13 07:26:47 +03:00
|
|
|
if (pos->lockowner != current->files)
|
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
|
|
|
|
*
|
2015-09-04 22:07:37 +03:00
|
|
|
* Ensure that the attributes are up to date if we're mounted
|
|
|
|
* with close-to-open semantics and we have cached data that will
|
|
|
|
* need to be revalidated on open.
|
2009-03-19 22:35:50 +03:00
|
|
|
*/
|
|
|
|
void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
|
|
|
|
{
|
2015-09-04 22:07:37 +03:00
|
|
|
struct nfs_inode *nfsi;
|
2009-03-19 22:35:50 +03:00
|
|
|
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);
|
2016-12-17 01:39:58 +03:00
|
|
|
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
|
|
|
|
return;
|
2015-09-04 22:07:37 +03:00
|
|
|
nfsi = NFS_I(inode);
|
|
|
|
if (inode->i_mapping->nrpages == 0)
|
|
|
|
return;
|
|
|
|
if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
|
|
|
|
return;
|
|
|
|
if (!list_empty(&nfsi->open_files))
|
2009-03-19 22:35:50 +03:00
|
|
|
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
|
|
|
|
2016-10-13 07:26:47 +03:00
|
|
|
struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
|
|
|
|
fmode_t f_mode,
|
|
|
|
struct file *filp)
|
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;
|
2016-10-13 07:26:47 +03:00
|
|
|
ctx->flock_owner = (fl_owner_t)filp;
|
2011-10-24 02:49:54 +04:00
|
|
|
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
|
|
|
|
2015-09-04 22:17:53 +03:00
|
|
|
static void put_nfs_open_context_sync(struct nfs_open_context *ctx)
|
|
|
|
{
|
|
|
|
__put_nfs_open_context(ctx, 1);
|
|
|
|
}
|
|
|
|
|
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);
|
2016-06-09 00:08:28 +03:00
|
|
|
if (ctx->mode & FMODE_WRITE)
|
|
|
|
list_add(&ctx->list, &nfsi->open_files);
|
|
|
|
else
|
|
|
|
list_add_tail(&ctx->list, &nfsi->open_files);
|
2005-04-17 02:20:36 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-07-13 21:01:33 +03:00
|
|
|
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
|
|
|
|
2015-12-05 10:57:31 +03:00
|
|
|
/*
|
|
|
|
* We fatal error on write before. Try to writeback
|
|
|
|
* every page again.
|
|
|
|
*/
|
|
|
|
if (ctx->error < 0)
|
|
|
|
invalidate_inode_pages2(inode->i_mapping);
|
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);
|
2015-09-04 22:17:53 +03:00
|
|
|
put_nfs_open_context_sync(ctx);
|
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;
|
|
|
|
|
2016-10-13 07:26:47 +03:00
|
|
|
ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode, filp);
|
2011-10-24 02:49:54 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
2016-07-05 20:46:53 +03:00
|
|
|
/* pNFS: Attributes aren't updated until we layoutcommit */
|
|
|
|
if (S_ISREG(inode->i_mode)) {
|
|
|
|
status = pnfs_sync_inode(inode, false);
|
|
|
|
if (status)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-12-05 02:34:34 +03:00
|
|
|
bool nfs_mapping_need_revalidate_inode(struct inode *inode)
|
2012-04-29 19:23:50 +04:00
|
|
|
{
|
2016-12-09 02:18:38 +03:00
|
|
|
return nfs_check_cache_invalid(inode, NFS_INO_REVAL_PAGECACHE) ||
|
|
|
|
NFS_STALE(inode);
|
2012-04-29 19:23:50 +04:00
|
|
|
}
|
|
|
|
|
2015-11-18 05:14:24 +03:00
|
|
|
int nfs_revalidate_mapping_rcu(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
unsigned long *bitlock = &nfsi->flags;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (IS_SWAPFILE(inode))
|
|
|
|
goto out;
|
|
|
|
if (nfs_mapping_need_revalidate_inode(inode)) {
|
|
|
|
ret = -ECHILD;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
if (test_bit(NFS_INO_INVALIDATING, bitlock) ||
|
|
|
|
(nfsi->cache_validity & NFS_INO_INVALID_DATA))
|
|
|
|
ret = -ECHILD;
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-01-24 22:54:55 +03:00
|
|
|
/**
|
2016-06-22 15:19:36 +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
|
|
|
|
*/
|
2016-06-22 15:19:36 +03:00
|
|
|
int nfs_revalidate_mapping(struct inode *inode,
|
|
|
|
struct address_space *mapping)
|
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);
|
2016-06-22 15:19:36 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-06-09 00:08:28 +03:00
|
|
|
static bool nfs_file_has_writers(struct nfs_inode *nfsi)
|
2015-03-03 07:32:08 +03:00
|
|
|
{
|
2016-06-09 00:08:28 +03:00
|
|
|
struct inode *inode = &nfsi->vfs_inode;
|
|
|
|
|
|
|
|
assert_spin_locked(&inode->i_lock);
|
|
|
|
|
|
|
|
if (!S_ISREG(inode->i_mode))
|
|
|
|
return false;
|
|
|
|
if (list_empty(&nfsi->open_files))
|
|
|
|
return false;
|
|
|
|
/* Note: This relies on nfsi->open_files being ordered with writers
|
|
|
|
* being placed at the head of the list.
|
|
|
|
* See nfs_inode_attach_open_context()
|
|
|
|
*/
|
|
|
|
return (list_first_entry(&nfsi->open_files,
|
|
|
|
struct nfs_open_context,
|
|
|
|
list)->mode & FMODE_WRITE) == FMODE_WRITE;
|
2015-03-03 07:32:08 +03:00
|
|
|
}
|
|
|
|
|
2016-06-26 00:24:46 +03:00
|
|
|
static bool nfs_file_has_buffered_writers(struct nfs_inode *nfsi)
|
2015-03-03 07:32:08 +03:00
|
|
|
{
|
2016-06-26 00:24:46 +03:00
|
|
|
return nfs_file_has_writers(nfsi) && nfs_file_io_is_buffered(nfsi);
|
2015-03-03 07:32:08 +03:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2016-06-26 00:24:46 +03:00
|
|
|
if (!nfs_file_has_buffered_writers(nfsi)) {
|
2016-06-09 00:08:28 +03:00
|
|
|
/* Verify a few of the more important attributes */
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && inode->i_version != fattr->change_attr)
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_REVAL_PAGECACHE;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2016-06-09 00:08:28 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime))
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR;
|
2006-03-20 21:44:07 +03:00
|
|
|
|
2016-06-09 00:08:28 +03:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CTIME) && !timespec_equal(&inode->i_ctime, &fattr->ctime))
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR;
|
|
|
|
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
|
|
|
|
cur_size = i_size_read(inode);
|
|
|
|
new_isize = nfs_size_to_loff_t(fattr->size);
|
|
|
|
if (cur_size != new_isize)
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
|
|
|
|
}
|
2009-03-11 21:10:24 +03:00
|
|
|
}
|
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)
|
2016-10-28 01:42:04 +03:00
|
|
|
nfs_set_cache_invalid(inode, invalid | NFS_INO_REVAL_FORCED);
|
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-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 ||
|
|
|
|
((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0);
|
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);
|
|
|
|
|
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)
|
|
|
|
{
|
2016-06-09 00:08:28 +03:00
|
|
|
unsigned long invalid = NFS_INO_INVALID_ATTR;
|
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;
|
2016-06-26 00:24:46 +03:00
|
|
|
bool have_writers = nfs_file_has_buffered_writers(nfsi);
|
2016-07-28 21:41:10 +03:00
|
|
|
bool cache_revalidated = true;
|
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
|
|
|
|
2016-07-28 21:41:10 +03:00
|
|
|
if (pnfs_layoutcommit_outstanding(inode)) {
|
|
|
|
nfsi->cache_validity |= save_cache_validity & NFS_INO_INVALID_ATTR;
|
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
2016-07-18 07:51:01 +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);
|
2016-06-08 04:44:08 +03:00
|
|
|
/* Could it be a race with writeback? */
|
2016-06-09 00:08:28 +03:00
|
|
|
if (!have_writers) {
|
2016-06-08 04:44:08 +03:00
|
|
|
invalid |= NFS_INO_INVALID_ATTR
|
|
|
|
| NFS_INO_INVALID_DATA
|
|
|
|
| NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL;
|
|
|
|
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
|
|
|
}
|
2015-12-30 02:55:19 +03:00
|
|
|
} else {
|
2014-04-15 18:07:57 +04:00
|
|
|
nfsi->cache_validity |= save_cache_validity;
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
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));
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
|
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));
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
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? */
|
2016-06-09 00:08:28 +03:00
|
|
|
if (nfsi->nrequests == 0 || new_isize > cur_isize) {
|
2009-03-11 21:10:24 +03:00
|
|
|
i_size_write(inode, new_isize);
|
2016-06-09 00:08:28 +03:00
|
|
|
if (!have_writers)
|
|
|
|
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
|
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
|
|
|
}
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
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));
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
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;
|
|
|
|
}
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
|
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;
|
|
|
|
}
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
2009-08-09 23:06:19 +04:00
|
|
|
|
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;
|
|
|
|
}
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
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
|
|
|
}
|
2015-12-30 02:55:19 +03: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);
|
2015-12-30 02:55:19 +03:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
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);
|
2015-12-30 02:55:19 +03:00
|
|
|
} else if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
|
2009-03-11 21:10:24 +03:00
|
|
|
inode->i_blocks = fattr->du.nfs2.blocks;
|
2015-12-30 02:55:19 +03:00
|
|
|
else
|
|
|
|
cache_revalidated = false;
|
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 {
|
2015-12-30 02:55:19 +03:00
|
|
|
if (cache_revalidated) {
|
|
|
|
if (!time_in_range_open(now, nfsi->attrtimeo_timestamp,
|
|
|
|
nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
|
|
|
|
nfsi->attrtimeo <<= 1;
|
|
|
|
if (nfsi->attrtimeo > NFS_MAXATTRTIMEO(inode))
|
|
|
|
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
|
|
|
|
}
|
2007-10-02 02:57:50 +04:00
|
|
|
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
|
|
|
}
|
2015-11-25 21:50:11 +03:00
|
|
|
|
|
|
|
/* Don't declare attrcache up to date if there were no attrs! */
|
2015-12-30 02:55:19 +03:00
|
|
|
if (cache_revalidated)
|
2015-11-25 21:50:11 +03: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);
|
2016-04-29 06:56:31 +03:00
|
|
|
init_rwsem(&nfsi->rmdir_sem);
|
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|
|
2016-01-15 02:18:21 +03:00
|
|
|
SLAB_MEM_SPREAD|SLAB_ACCOUNT),
|
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);
|
|
|
|
}
|
|
|
|
|
netns: make struct pernet_operations::id unsigned int
Make struct pernet_operations::id unsigned.
There are 2 reasons to do so:
1)
This field is really an index into an zero based array and
thus is unsigned entity. Using negative value is out-of-bound
access by definition.
2)
On x86_64 unsigned 32-bit data which are mixed with pointers
via array indexing or offsets added or subtracted to pointers
are preffered to signed 32-bit data.
"int" being used as an array index needs to be sign-extended
to 64-bit before being used.
void f(long *p, int i)
{
g(p[i]);
}
roughly translates to
movsx rsi, esi
mov rdi, [rsi+...]
call g
MOVSX is 3 byte instruction which isn't necessary if the variable is
unsigned because x86_64 is zero extending by default.
Now, there is net_generic() function which, you guessed it right, uses
"int" as an array index:
static inline void *net_generic(const struct net *net, int id)
{
...
ptr = ng->ptr[id - 1];
...
}
And this function is used a lot, so those sign extensions add up.
Patch snipes ~1730 bytes on allyesconfig kernel (without all junk
messing with code generation):
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
Unfortunately some functions actually grow bigger.
This is a semmingly random artefact of code generation with register
allocator being used differently. gcc decides that some variable
needs to live in new r8+ registers and every access now requires REX
prefix. Or it is shifted into r12, so [r12+0] addressing mode has to be
used which is longer than [r8]
However, overall balance is in negative direction:
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
function old new delta
nfsd4_lock 3886 3959 +73
tipc_link_build_proto_msg 1096 1140 +44
mac80211_hwsim_new_radio 2776 2808 +32
tipc_mon_rcv 1032 1058 +26
svcauth_gss_legacy_init 1413 1429 +16
tipc_bcbase_select_primary 379 392 +13
nfsd4_exchange_id 1247 1260 +13
nfsd4_setclientid_confirm 782 793 +11
...
put_client_renew_locked 494 480 -14
ip_set_sockfn_get 730 716 -14
geneve_sock_add 829 813 -16
nfsd4_sequence_done 721 703 -18
nlmclnt_lookup_host 708 686 -22
nfsd4_lockt 1085 1063 -22
nfs_get_client 1077 1050 -27
tcf_bpf_init 1106 1076 -30
nfsd4_encode_fattr 5997 5930 -67
Total: Before=154856051, After=154854321, chg -0.00%
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-11-17 04:58:21 +03:00
|
|
|
unsigned 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
|
|
|
|
2011-12-06 17:42:40 +04:00
|
|
|
rpc_proc_register(&init_net, &nfs_rpcstat);
|
2015-07-01 06:58:31 +03:00
|
|
|
|
|
|
|
err = register_nfs_fs();
|
|
|
|
if (err)
|
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:
|
2011-12-06 17:42:40 +04:00
|
|
|
rpc_proc_unregister(&init_net, "nfs");
|
2005-04-17 02:20:36 +04:00
|
|
|
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);
|
2011-12-06 17:42:40 +04:00
|
|
|
rpc_proc_unregister(&init_net, "nfs");
|
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)
|