2019-05-19 05:08:20 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* linux/fs/nfs/inode.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992 Rick Sladkey
|
|
|
|
*
|
|
|
|
* nfs inode and superblock handling functions
|
|
|
|
*
|
2008-10-27 08:19:48 -07:00
|
|
|
* Modularised by Alan Cox <alan@lxorguk.ukuu.org.uk>, while hacking some
|
2005-04-16 15:20:36 -07: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 11:15:33 -07:00
|
|
|
#include <linux/sched/signal.h>
|
2005-04-16 15:20:36 -07: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 11:44:22 -07:00
|
|
|
#include <linux/sunrpc/metrics.h>
|
2005-04-16 15:20:36 -07: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 06:34:28 -07: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 01:04:11 -07:00
|
|
|
#include <linux/slab.h>
|
2010-12-02 12:31:23 -07:00
|
|
|
#include <linux/compat.h>
|
2011-12-01 14:44:39 -07:00
|
|
|
#include <linux/freezer.h>
|
2016-12-24 12:46:01 -07:00
|
|
|
#include <linux/uaccess.h>
|
2018-01-09 06:21:17 -07:00
|
|
|
#include <linux/iversion.h>
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2005-06-22 10:16:21 -07:00
|
|
|
#include "nfs4_fs.h"
|
2006-01-03 01:55:41 -07:00
|
|
|
#include "callback.h"
|
2005-04-16 15:20:36 -07:00
|
|
|
#include "delegation.h"
|
2006-03-20 11:44:13 -07: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 06:34:33 -07:00
|
|
|
#include "internal.h"
|
2009-04-03 08:42:42 -07:00
|
|
|
#include "fscache.h"
|
2010-10-19 21:18:01 -07:00
|
|
|
#include "pnfs.h"
|
2012-07-30 13:05:16 -07:00
|
|
|
#include "nfs.h"
|
2011-11-25 07:13:04 -07:00
|
|
|
#include "netns.h"
|
2019-01-24 14:10:46 -07:00
|
|
|
#include "sysfs.h"
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-08-19 15:59:33 -07:00
|
|
|
#include "nfstrace.h"
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
#define NFSDBG_FACILITY NFSDBG_VFS
|
|
|
|
|
2007-10-09 09:01:04 -07:00
|
|
|
#define NFS_64_BIT_INODE_NUMBERS_ENABLED 1
|
|
|
|
|
|
|
|
/* Default is to see 64-bit inode numbers */
|
2012-01-12 16:02:20 -07:00
|
|
|
static bool enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
|
2007-10-09 09:01:04 -07:00
|
|
|
|
2005-12-03 13:20:07 -07:00
|
|
|
static int nfs_update_inode(struct inode *, struct nfs_fattr *);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2006-12-06 21:33:20 -07:00
|
|
|
static struct kmem_cache * nfs_inode_cachep;
|
2005-06-22 10:16:27 -07:00
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
static inline unsigned long
|
|
|
|
nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
return nfs_fileid_to_ino_t(fattr->fileid);
|
|
|
|
}
|
|
|
|
|
freezer,sched: Rewrite core freezer logic
Rewrite the core freezer to behave better wrt thawing and be simpler
in general.
By replacing PF_FROZEN with TASK_FROZEN, a special block state, it is
ensured frozen tasks stay frozen until thawed and don't randomly wake
up early, as is currently possible.
As such, it does away with PF_FROZEN and PF_FREEZER_SKIP, freeing up
two PF_flags (yay!).
Specifically; the current scheme works a little like:
freezer_do_not_count();
schedule();
freezer_count();
And either the task is blocked, or it lands in try_to_freezer()
through freezer_count(). Now, when it is blocked, the freezer
considers it frozen and continues.
However, on thawing, once pm_freezing is cleared, freezer_count()
stops working, and any random/spurious wakeup will let a task run
before its time.
That is, thawing tries to thaw things in explicit order; kernel
threads and workqueues before doing bringing SMP back before userspace
etc.. However due to the above mentioned races it is entirely possible
for userspace tasks to thaw (by accident) before SMP is back.
This can be a fatal problem in asymmetric ISA architectures (eg ARMv9)
where the userspace task requires a special CPU to run.
As said; replace this with a special task state TASK_FROZEN and add
the following state transitions:
TASK_FREEZABLE -> TASK_FROZEN
__TASK_STOPPED -> TASK_FROZEN
__TASK_TRACED -> TASK_FROZEN
The new TASK_FREEZABLE can be set on any state part of TASK_NORMAL
(IOW. TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE) -- any such state
is already required to deal with spurious wakeups and the freezer
causes one such when thawing the task (since the original state is
lost).
The special __TASK_{STOPPED,TRACED} states *can* be restored since
their canonical state is in ->jobctl.
With this, frozen tasks need an explicit TASK_FROZEN wakeup and are
free of undue (early / spurious) wakeups.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/20220822114649.055452969@infradead.org
2022-08-22 04:18:22 -07:00
|
|
|
int nfs_wait_bit_killable(struct wait_bit_key *key, int mode)
|
2009-03-11 11:10:30 -07:00
|
|
|
{
|
freezer,sched: Rewrite core freezer logic
Rewrite the core freezer to behave better wrt thawing and be simpler
in general.
By replacing PF_FROZEN with TASK_FROZEN, a special block state, it is
ensured frozen tasks stay frozen until thawed and don't randomly wake
up early, as is currently possible.
As such, it does away with PF_FROZEN and PF_FREEZER_SKIP, freeing up
two PF_flags (yay!).
Specifically; the current scheme works a little like:
freezer_do_not_count();
schedule();
freezer_count();
And either the task is blocked, or it lands in try_to_freezer()
through freezer_count(). Now, when it is blocked, the freezer
considers it frozen and continues.
However, on thawing, once pm_freezing is cleared, freezer_count()
stops working, and any random/spurious wakeup will let a task run
before its time.
That is, thawing tries to thaw things in explicit order; kernel
threads and workqueues before doing bringing SMP back before userspace
etc.. However due to the above mentioned races it is entirely possible
for userspace tasks to thaw (by accident) before SMP is back.
This can be a fatal problem in asymmetric ISA architectures (eg ARMv9)
where the userspace task requires a special CPU to run.
As said; replace this with a special task state TASK_FROZEN and add
the following state transitions:
TASK_FREEZABLE -> TASK_FROZEN
__TASK_STOPPED -> TASK_FROZEN
__TASK_TRACED -> TASK_FROZEN
The new TASK_FREEZABLE can be set on any state part of TASK_NORMAL
(IOW. TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE) -- any such state
is already required to deal with spurious wakeups and the freezer
causes one such when thawing the task (since the original state is
lost).
The special __TASK_{STOPPED,TRACED} states *can* be restored since
their canonical state is in ->jobctl.
With this, frozen tasks need an explicit TASK_FROZEN wakeup and are
free of undue (early / spurious) wakeups.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/20220822114649.055452969@infradead.org
2022-08-22 04:18:22 -07:00
|
|
|
schedule();
|
2015-12-13 14:11:16 -07:00
|
|
|
if (signal_pending_state(mode, current))
|
|
|
|
return -ERESTARTSYS;
|
2009-03-11 11:10:30 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_wait_bit_killable);
|
2009-03-11 11:10:30 -07:00
|
|
|
|
2007-10-09 09:01:04 -07: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 12:31:23 -07:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
compat_ulong_t ino;
|
|
|
|
#else
|
|
|
|
unsigned long ino;
|
|
|
|
#endif
|
2007-10-09 09:01:04 -07: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 12:36:36 -07:00
|
|
|
int nfs_drop_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
return NFS_STALE(inode) || generic_drop_inode(inode);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_drop_inode);
|
|
|
|
|
2012-07-30 13:05:21 -07:00
|
|
|
void nfs_clear_inode(struct inode *inode)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2006-06-01 14:26:35 -07:00
|
|
|
/*
|
|
|
|
* The following should never happen...
|
|
|
|
*/
|
2012-10-15 13:19:30 -07:00
|
|
|
WARN_ON_ONCE(nfs_have_writebacks(inode));
|
|
|
|
WARN_ON_ONCE(!list_empty(&NFS_I(inode)->open_files));
|
2005-06-22 10:16:22 -07:00
|
|
|
nfs_zap_acl_cache(inode);
|
2006-07-25 08:28:18 -07:00
|
|
|
nfs_access_zap_cache(inode);
|
2013-09-27 03:20:03 -07:00
|
|
|
nfs_fscache_clear_inode(inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_clear_inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2010-06-07 11:34:48 -07:00
|
|
|
void nfs_evict_inode(struct inode *inode)
|
|
|
|
{
|
2014-04-03 14:47:49 -07:00
|
|
|
truncate_inode_pages_final(&inode->i_data);
|
2012-05-03 05:48:02 -07:00
|
|
|
clear_inode(inode);
|
2010-06-07 11:34:48 -07:00
|
|
|
nfs_clear_inode(inode);
|
|
|
|
}
|
|
|
|
|
2015-03-25 14:23:31 -07:00
|
|
|
int nfs_sync_inode(struct inode *inode)
|
2015-03-25 13:38:33 -07:00
|
|
|
{
|
2016-03-02 09:35:55 -07:00
|
|
|
inode_dio_wait(inode);
|
2015-03-25 13:38:33 -07:00
|
|
|
return nfs_wb_all(inode);
|
|
|
|
}
|
2015-03-25 14:23:31 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_sync_inode);
|
2015-03-25 13:38:33 -07:00
|
|
|
|
2005-12-13 14:13:54 -07:00
|
|
|
/**
|
|
|
|
* nfs_sync_mapping - helper to flush all mmapped dirty data to disk
|
2019-02-18 11:32:38 -07:00
|
|
|
* @mapping: pointer to struct address_space
|
2005-12-13 14:13:54 -07:00
|
|
|
*/
|
|
|
|
int nfs_sync_mapping(struct address_space *mapping)
|
|
|
|
{
|
2010-02-19 18:03:29 -07:00
|
|
|
int ret = 0;
|
2005-12-13 14:13:54 -07:00
|
|
|
|
2010-02-19 18:03:29 -07:00
|
|
|
if (mapping->nrpages != 0) {
|
|
|
|
unmap_mapping_range(mapping, 0, 0, 0);
|
|
|
|
ret = nfs_wb_all(mapping->host);
|
|
|
|
}
|
2005-12-13 14:13:54 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-12-16 16:51:15 -07: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);
|
|
|
|
}
|
|
|
|
|
2021-03-25 18:07:21 -07:00
|
|
|
static bool nfs_check_cache_flags_invalid(struct inode *inode,
|
|
|
|
unsigned long flags)
|
2016-12-08 16:18:38 -07:00
|
|
|
{
|
|
|
|
unsigned long cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
|
|
|
|
|
|
|
|
return (cache_validity & flags) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nfs_check_cache_invalid(struct inode *inode, unsigned long flags)
|
|
|
|
{
|
2021-03-25 18:07:21 -07:00
|
|
|
if (nfs_check_cache_flags_invalid(inode, flags))
|
|
|
|
return true;
|
|
|
|
return nfs_attribute_cache_expired(inode);
|
2016-12-08 16:18:38 -07:00
|
|
|
}
|
2020-06-23 15:39:04 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_check_cache_invalid);
|
2016-12-08 16:18:38 -07:00
|
|
|
|
2021-02-08 14:45:49 -07:00
|
|
|
#ifdef CONFIG_NFS_V4_2
|
|
|
|
static bool nfs_has_xattr_cache(const struct nfs_inode *nfsi)
|
|
|
|
{
|
|
|
|
return nfsi->xattr_cache != NULL;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static bool nfs_has_xattr_cache(const struct nfs_inode *nfsi)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-03-08 12:42:53 -07:00
|
|
|
void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
|
2014-06-20 10:11:01 -07:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2018-06-03 10:05:21 -07:00
|
|
|
bool have_delegation = NFS_PROTO(inode)->have_delegation(inode, FMODE_READ);
|
|
|
|
|
|
|
|
if (have_delegation) {
|
|
|
|
if (!(flags & NFS_INO_REVAL_FORCED))
|
2021-04-13 06:41:16 -07:00
|
|
|
flags &= ~(NFS_INO_INVALID_MODE |
|
2021-04-11 11:31:24 -07:00
|
|
|
NFS_INO_INVALID_OTHER |
|
|
|
|
NFS_INO_INVALID_XATTR);
|
|
|
|
flags &= ~(NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_SIZE);
|
2022-02-09 11:26:19 -07:00
|
|
|
}
|
2014-06-20 10:11:01 -07:00
|
|
|
|
2021-02-08 14:45:49 -07:00
|
|
|
if (!nfs_has_xattr_cache(nfsi))
|
|
|
|
flags &= ~NFS_INO_INVALID_XATTR;
|
2021-03-26 13:23:39 -07:00
|
|
|
if (flags & NFS_INO_INVALID_DATA)
|
nfs: Convert to new fscache volume/cookie API
Change the nfs filesystem to support fscache's indexing rewrite and
reenable caching in nfs.
The following changes have been made:
(1) The fscache_netfs struct is no more, and there's no need to register
the filesystem as a whole.
(2) The session cookie is now an fscache_volume cookie, allocated with
fscache_acquire_volume(). That takes three parameters: a string
representing the "volume" in the index, a string naming the cache to
use (or NULL) and a u64 that conveys coherency metadata for the
volume.
For nfs, I've made it render the volume name string as:
"nfs,<ver>,<family>,<address>,<port>,<fsidH>,<fsidL>*<,param>[,<uniq>]"
(3) The fscache_cookie_def is no more and needed information is passed
directly to fscache_acquire_cookie(). The cache no longer calls back
into the filesystem, but rather metadata changes are indicated at
other times.
fscache_acquire_cookie() is passed the same keying and coherency
information as before.
(4) fscache_enable/disable_cookie() have been removed.
Call fscache_use_cookie() and fscache_unuse_cookie() when a file is
opened or closed to prevent a cache file from being culled and to keep
resources to hand that are needed to do I/O.
If a file is opened for writing, we invalidate it with
FSCACHE_INVAL_DIO_WRITE in lieu of doing writeback to the cache,
thereby making it cease caching until all currently open files are
closed. This should give the same behaviour as the uptream code.
Making the cache store local modifications isn't straightforward for
NFS, so that's left for future patches.
(5) fscache_invalidate() now needs to be given uptodate auxiliary data and
a file size. It also takes a flag to indicate if this was due to a
DIO write.
(6) Call nfs_fscache_invalidate() with FSCACHE_INVAL_DIO_WRITE on a file
to which a DIO write is made.
(7) Call fscache_note_page_release() from nfs_release_page().
(8) Use a killable wait in nfs_vm_page_mkwrite() when waiting for
PG_fscache to be cleared.
(9) The functions to read and write data to/from the cache are stubbed out
pending a conversion to use netfslib.
Changes
=======
ver #3:
- Added missing =n fallback for nfs_fscache_release_file()[1][2].
ver #2:
- Use gfpflags_allow_blocking() rather than using flag directly.
- fscache_acquire_volume() now returns errors.
- Remove NFS_INO_FSCACHE as it's no longer used.
- Need to unuse a cookie on file-release, not inode-clear.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
Co-developed-by: David Howells <dhowells@redhat.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Dave Wysochanski <dwysocha@redhat.com>
Acked-by: Jeff Layton <jlayton@kernel.org>
cc: Trond Myklebust <trond.myklebust@hammerspace.com>
cc: Anna Schumaker <anna.schumaker@netapp.com>
cc: linux-nfs@vger.kernel.org
cc: linux-cachefs@redhat.com
Link: https://lore.kernel.org/r/202112100804.nksO8K4u-lkp@intel.com/ [1]
Link: https://lore.kernel.org/r/202112100957.2oEDT20W-lkp@intel.com/ [2]
Link: https://lore.kernel.org/r/163819668938.215744.14448852181937731615.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/163906979003.143852.2601189243864854724.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163967182112.1823006.7791504655391213379.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/164021575950.640689.12069642327533368467.stgit@warthog.procyon.org.uk/ # v4
2020-11-14 11:43:54 -07:00
|
|
|
nfs_fscache_invalidate(inode, 0);
|
2022-02-09 11:26:19 -07:00
|
|
|
flags &= ~NFS_INO_REVAL_FORCED;
|
2021-09-28 08:15:53 -07:00
|
|
|
|
2014-06-20 10:11:01 -07:00
|
|
|
nfsi->cache_validity |= flags;
|
2021-09-28 08:15:53 -07:00
|
|
|
|
2023-03-21 15:27:04 -07:00
|
|
|
if (inode->i_mapping->nrpages == 0) {
|
|
|
|
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
|
|
|
|
nfs_ooo_clear(nfsi);
|
|
|
|
} else if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
|
|
|
|
nfs_ooo_clear(nfsi);
|
|
|
|
}
|
2021-11-16 06:49:24 -07:00
|
|
|
trace_nfs_set_cache_invalid(inode, 0);
|
2014-06-20 10:11:01 -07:00
|
|
|
}
|
2021-03-08 12:42:55 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_set_cache_invalid);
|
2014-06-20 10:11:01 -07:00
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* Invalidate the local caches
|
|
|
|
*/
|
2005-11-25 15:10:06 -07:00
|
|
|
static void nfs_zap_caches_locked(struct inode *inode)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
int mode = inode->i_mode;
|
|
|
|
|
2006-03-20 11:44:14 -07:00
|
|
|
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
|
|
|
|
|
2007-09-28 16:22:40 -07:00
|
|
|
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
|
|
|
|
nfsi->attrtimeo_timestamp = jiffies;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2022-02-09 11:22:48 -07:00
|
|
|
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR |
|
|
|
|
NFS_INO_INVALID_DATA |
|
|
|
|
NFS_INO_INVALID_ACCESS |
|
|
|
|
NFS_INO_INVALID_ACL |
|
|
|
|
NFS_INO_INVALID_XATTR);
|
|
|
|
else
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR |
|
|
|
|
NFS_INO_INVALID_ACCESS |
|
|
|
|
NFS_INO_INVALID_ACL |
|
|
|
|
NFS_INO_INVALID_XATTR);
|
2014-02-06 12:38:53 -07:00
|
|
|
nfs_zap_label_cache_locked(nfsi);
|
2005-11-25 15:10:06 -07:00
|
|
|
}
|
2005-08-18 11:24:12 -07:00
|
|
|
|
2005-11-25 15:10:06 -07:00
|
|
|
void nfs_zap_caches(struct inode *inode)
|
|
|
|
{
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
nfs_zap_caches_locked(inode);
|
2005-08-18 11:24:12 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2005-06-22 10:16:22 -07:00
|
|
|
}
|
|
|
|
|
2006-10-19 23:28:40 -07:00
|
|
|
void nfs_zap_mapping(struct inode *inode, struct address_space *mapping)
|
|
|
|
{
|
|
|
|
if (mapping->nrpages != 0) {
|
|
|
|
spin_lock(&inode->i_lock);
|
2014-06-20 10:11:01 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
|
2006-10-19 23:28:40 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-11 14:39:04 -07:00
|
|
|
void nfs_zap_acl_cache(struct inode *inode)
|
2005-06-22 10:16:22 -07: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 11:24:12 -07:00
|
|
|
spin_lock(&inode->i_lock);
|
2005-08-18 11:24:09 -07:00
|
|
|
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ACL;
|
2005-08-18 11:24:12 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2012-07-30 13:05:24 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_zap_acl_cache);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2007-09-28 14:11:45 -07:00
|
|
|
void nfs_invalidate_atime(struct inode *inode)
|
|
|
|
{
|
|
|
|
spin_lock(&inode->i_lock);
|
2014-06-20 10:11:01 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
|
2007-09-28 14:11:45 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_invalidate_atime);
|
2007-09-28 14:11:45 -07:00
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
2005-11-25 15:10:06 -07:00
|
|
|
* Invalidate, but do not unhash, the inode.
|
|
|
|
* NB: must be called with inode->i_lock held!
|
2005-04-16 15:20:36 -07:00
|
|
|
*/
|
2020-04-06 10:39:29 -07:00
|
|
|
static void nfs_set_inode_stale_locked(struct inode *inode)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2008-01-22 23:58:59 -07:00
|
|
|
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
|
2005-11-25 15:10:06 -07:00
|
|
|
nfs_zap_caches_locked(inode);
|
2020-04-06 10:39:29 -07:00
|
|
|
trace_nfs_set_inode_stale(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfs_set_inode_stale(struct inode *inode)
|
|
|
|
{
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
nfs_set_inode_stale_locked(inode);
|
|
|
|
spin_unlock(&inode->i_lock);
|
2005-04-16 15:20:36 -07: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)
|
|
|
|
{
|
2022-06-14 22:39:24 -07:00
|
|
|
struct nfs_find_desc *desc = opaque;
|
2005-04-16 15:20:36 -07:00
|
|
|
struct nfs_fh *fh = desc->fh;
|
|
|
|
struct nfs_fattr *fattr = desc->fattr;
|
|
|
|
|
|
|
|
if (NFS_FILEID(inode) != fattr->fileid)
|
|
|
|
return 0;
|
2021-03-01 18:37:10 -07:00
|
|
|
if (inode_wrong_type(inode, fattr->mode))
|
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-27 18:10:34 -07:00
|
|
|
return 0;
|
2005-04-16 15:20:36 -07: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)
|
|
|
|
{
|
2022-06-14 22:39:24 -07:00
|
|
|
struct nfs_find_desc *desc = opaque;
|
2005-04-16 15:20:36 -07:00
|
|
|
struct nfs_fattr *fattr = desc->fattr;
|
|
|
|
|
2008-01-22 23:59:08 -07:00
|
|
|
set_nfs_fileid(inode, fattr->fileid);
|
2016-06-17 13:48:28 -07:00
|
|
|
inode->i_mode = fattr->mode;
|
2005-04-16 15:20:36 -07:00
|
|
|
nfs_copy_fh(NFS_FH(inode), desc->fh);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-22 09:50:40 -07:00
|
|
|
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
|
2014-02-06 12:38:53 -07: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);
|
|
|
|
}
|
|
|
|
|
2021-10-22 10:11:12 -07:00
|
|
|
void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr)
|
2013-05-22 09:50:44 -07:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
2021-10-22 10:11:12 -07:00
|
|
|
if (fattr->label == NULL)
|
2013-05-22 09:50:44 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
|
2021-10-22 10:11:12 -07:00
|
|
|
error = security_inode_notifysecctx(inode, fattr->label->label,
|
|
|
|
fattr->label->len);
|
2013-05-22 09:50:44 -07:00
|
|
|
if (error)
|
|
|
|
printk(KERN_ERR "%s() %s %d "
|
|
|
|
"security_inode_notifysecctx() %d\n",
|
|
|
|
__func__,
|
2021-10-22 10:11:12 -07:00
|
|
|
(char *)fattr->label->label,
|
|
|
|
fattr->label->len, error);
|
2014-02-06 12:38:53 -07:00
|
|
|
nfs_clear_label_invalid(inode);
|
2013-05-22 09:50:44 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-22 09:50:40 -07:00
|
|
|
struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags)
|
|
|
|
{
|
2021-11-05 10:40:11 -07:00
|
|
|
struct nfs4_label *label;
|
2013-05-22 09:50:40 -07:00
|
|
|
|
|
|
|
if (!(server->caps & NFS_CAP_SECURITY_LABEL))
|
2021-11-05 10:40:11 -07:00
|
|
|
return NULL;
|
2013-05-22 09:50:40 -07:00
|
|
|
|
|
|
|
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 09:50:44 -07:00
|
|
|
#else
|
2021-10-22 10:11:12 -07:00
|
|
|
void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr)
|
2013-05-22 09:50:44 -07:00
|
|
|
{
|
|
|
|
}
|
2013-05-22 09:50:40 -07:00
|
|
|
#endif
|
2013-05-22 09:50:44 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_setsecurity);
|
2013-05-22 09:50:40 -07:00
|
|
|
|
2017-06-29 06:34:51 -07:00
|
|
|
/* Search for inode identified by fh, fileid and i_mode in inode cache. */
|
|
|
|
struct inode *
|
|
|
|
nfs_ilookup(struct super_block *sb, struct nfs_fattr *fattr, struct nfs_fh *fh)
|
|
|
|
{
|
|
|
|
struct nfs_find_desc desc = {
|
|
|
|
.fh = fh,
|
|
|
|
.fattr = fattr,
|
|
|
|
};
|
|
|
|
struct inode *inode;
|
|
|
|
unsigned long hash;
|
|
|
|
|
|
|
|
if (!(fattr->valid & NFS_ATTR_FATTR_FILEID) ||
|
|
|
|
!(fattr->valid & NFS_ATTR_FATTR_TYPE))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
hash = nfs_fattr_to_ino_t(fattr);
|
|
|
|
inode = ilookup5(sb, hash, nfs_find_actor, &desc);
|
|
|
|
|
|
|
|
dprintk("%s: returning %p\n", __func__, inode);
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2021-09-28 14:41:41 -07:00
|
|
|
static void nfs_inode_init_regular(struct nfs_inode *nfsi)
|
|
|
|
{
|
|
|
|
atomic_long_set(&nfsi->nrequests, 0);
|
2022-08-13 05:22:25 -07:00
|
|
|
atomic_long_set(&nfsi->redirtied_pages, 0);
|
2021-09-28 14:41:41 -07:00
|
|
|
INIT_LIST_HEAD(&nfsi->commit_info.list);
|
|
|
|
atomic_long_set(&nfsi->commit_info.ncommit, 0);
|
|
|
|
atomic_set(&nfsi->commit_info.rpcs_out, 0);
|
|
|
|
mutex_init(&nfsi->commit_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs_inode_init_dir(struct nfs_inode *nfsi)
|
|
|
|
{
|
|
|
|
nfsi->cache_change_attribute = 0;
|
|
|
|
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
|
|
|
|
init_rwsem(&nfsi->rmdir_sem);
|
|
|
|
}
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* This is our front-end to iget that looks up inodes by file handle
|
|
|
|
* instead of inode number.
|
|
|
|
*/
|
|
|
|
struct inode *
|
2021-10-22 10:11:11 -07:00
|
|
|
nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
struct nfs_find_desc desc = {
|
|
|
|
.fh = fh,
|
|
|
|
.fattr = fattr
|
|
|
|
};
|
2006-03-20 11:44:48 -07:00
|
|
|
struct inode *inode = ERR_PTR(-ENOENT);
|
2018-03-05 13:01:18 -07:00
|
|
|
u64 fattr_supported = NFS_SB(sb)->fattr_valid;
|
2005-04-16 15:20:36 -07:00
|
|
|
unsigned long hash;
|
|
|
|
|
2011-03-24 10:12:30 -07: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-09 14:19:16 -07: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-16 15:20:36 -07:00
|
|
|
goto out_no_inode;
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0)
|
2005-04-16 15:20:36 -07:00
|
|
|
goto out_no_inode;
|
|
|
|
|
|
|
|
hash = nfs_fattr_to_ino_t(fattr);
|
|
|
|
|
2006-03-20 11:44:48 -07:00
|
|
|
inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc);
|
|
|
|
if (inode == NULL) {
|
|
|
|
inode = ERR_PTR(-ENOMEM);
|
2005-04-16 15:20:36 -07:00
|
|
|
goto out_no_inode;
|
2006-03-20 11:44:48 -07:00
|
|
|
}
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
if (inode->i_state & I_NEW) {
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2007-02-05 15:44:22 -07:00
|
|
|
unsigned long now = jiffies;
|
2005-04-16 15:20:36 -07: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;
|
2018-03-27 15:30:42 -07:00
|
|
|
nfsi->cache_validity = 0;
|
2009-08-09 12:06:19 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
|
2018-03-05 13:01:18 -07:00
|
|
|
&& (fattr_supported & NFS_ATTR_FATTR_MODE))
|
2021-04-13 06:41:16 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MODE);
|
2005-04-16 15:20:36 -07:00
|
|
|
/* Why so? Because we want revalidate for devices/FIFOs, and
|
|
|
|
* that's precisely what we have in nfs_file_inode_operations.
|
|
|
|
*/
|
2006-08-22 17:06:12 -07:00
|
|
|
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->file_inode_ops;
|
2005-04-16 15:20:36 -07: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 10:31:21 -07:00
|
|
|
inode->i_fop = NFS_SB(sb)->nfs_client->rpc_ops->file_ops;
|
2005-04-16 15:20:36 -07:00
|
|
|
inode->i_data.a_ops = &nfs_file_aops;
|
2021-09-28 14:41:41 -07:00
|
|
|
nfs_inode_init_regular(nfsi);
|
2005-04-16 15:20:36 -07:00
|
|
|
} else if (S_ISDIR(inode->i_mode)) {
|
2006-08-22 17:06:12 -07:00
|
|
|
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
|
2005-04-16 15:20:36 -07:00
|
|
|
inode->i_fop = &nfs_dir_operations;
|
2010-12-01 12:17:06 -07:00
|
|
|
inode->i_data.a_ops = &nfs_dir_aops;
|
2021-09-28 14:41:41 -07:00
|
|
|
nfs_inode_init_dir(nfsi);
|
2006-06-09 06:34:19 -07:00
|
|
|
/* Deal with crossing mountpoints */
|
2011-03-24 10:12:30 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
|
|
|
|
fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
|
2006-06-09 06:34:29 -07: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 06:34:19 -07:00
|
|
|
inode->i_fop = NULL;
|
2011-01-14 11:45:42 -07:00
|
|
|
inode->i_flags |= S_AUTOMOUNT;
|
2006-06-09 06:34:19 -07:00
|
|
|
}
|
2015-11-16 23:07:57 -07:00
|
|
|
} else if (S_ISLNK(inode->i_mode)) {
|
2005-04-16 15:20:36 -07:00
|
|
|
inode->i_op = &nfs_symlink_inode_operations;
|
2015-11-16 23:07:57 -07:00
|
|
|
inode_nohighmem(inode);
|
|
|
|
} else
|
2005-04-16 15:20:36 -07:00
|
|
|
init_special_inode(inode, inode->i_mode, fattr->rdev);
|
|
|
|
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_atime(inode, 0, 0);
|
|
|
|
inode_set_mtime(inode, 0, 0);
|
2023-07-05 12:01:22 -07:00
|
|
|
inode_set_ctime(inode, 0, 0);
|
2018-01-09 06:21:17 -07:00
|
|
|
inode_set_iversion_raw(inode, 0);
|
2009-03-11 11:10:24 -07:00
|
|
|
inode->i_size = 0;
|
2011-10-28 05:13:28 -07:00
|
|
|
clear_nlink(inode);
|
2013-02-01 15:26:23 -07:00
|
|
|
inode->i_uid = make_kuid(&init_user_ns, -2);
|
|
|
|
inode->i_gid = make_kgid(&init_user_ns, -2);
|
2009-03-11 11:10:24 -07:00
|
|
|
inode->i_blocks = 0;
|
2012-05-24 10:13:24 -07:00
|
|
|
nfsi->write_io = 0;
|
|
|
|
nfsi->read_io = 0;
|
2009-03-11 11:10:24 -07:00
|
|
|
|
2005-10-27 19:12:39 -07:00
|
|
|
nfsi->read_cache_jiffies = fattr->time_start;
|
2008-10-14 16:16:07 -07:00
|
|
|
nfsi->attr_gencount = fattr->gencount;
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_atime_to_ts(inode, fattr->atime);
|
2018-03-05 13:01:18 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_ATIME)
|
2018-03-20 13:53:31 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MTIME)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_mtime_to_ts(inode, fattr->mtime);
|
2018-03-05 13:01:18 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_MTIME)
|
2018-03-20 13:53:31 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
2023-07-05 12:01:22 -07:00
|
|
|
inode_set_ctime_to_ts(inode, fattr->ctime);
|
2018-03-05 13:01:18 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_CTIME)
|
2018-03-20 13:53:31 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CTIME);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
|
2018-01-09 06:21:17 -07:00
|
|
|
inode_set_iversion_raw(inode, fattr->change_attr);
|
2015-07-05 08:12:07 -07:00
|
|
|
else
|
2018-03-20 13:53:31 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SIZE)
|
|
|
|
inode->i_size = nfs_size_to_loff_t(fattr->size);
|
2009-08-09 12:06:19 -07:00
|
|
|
else
|
2018-03-20 13:53:31 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_SIZE);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_NLINK)
|
2011-10-28 05:13:29 -07:00
|
|
|
set_nlink(inode, fattr->nlink);
|
2018-03-05 13:01:18 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_NLINK)
|
2021-03-25 10:14:42 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_NLINK);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_OWNER)
|
|
|
|
inode->i_uid = fattr->uid;
|
2018-03-05 13:01:18 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_OWNER)
|
2018-03-20 13:53:31 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_OTHER);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_GROUP)
|
|
|
|
inode->i_gid = fattr->gid;
|
2018-03-05 13:01:18 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_GROUP)
|
2018-03-20 13:53:31 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_OTHER);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
|
|
|
|
inode->i_blocks = fattr->du.nfs2.blocks;
|
2018-03-05 13:01:18 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_BLOCKS_USED &&
|
|
|
|
fattr->size != 0)
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS);
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* report the blocks in 512byte units
|
|
|
|
*/
|
|
|
|
inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
|
2018-03-05 13:01:18 -07:00
|
|
|
} else if (fattr_supported & NFS_ATTR_FATTR_SPACE_USED &&
|
|
|
|
fattr->size != 0)
|
2021-03-24 09:29:41 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS);
|
2018-03-27 15:30:42 -07:00
|
|
|
|
2021-10-22 10:11:12 -07:00
|
|
|
nfs_setsecurity(inode, fattr);
|
2013-05-22 09:50:44 -07:00
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
|
2007-02-05 15:44:22 -07:00
|
|
|
nfsi->attrtimeo_timestamp = now;
|
2006-07-25 08:28:18 -07:00
|
|
|
nfsi->access_cache = RB_ROOT;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-09-27 03:20:03 -07:00
|
|
|
nfs_fscache_init_inode(inode);
|
2009-04-03 08:42:43 -07:00
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
unlock_new_inode(inode);
|
2017-07-02 22:27:26 -07:00
|
|
|
} else {
|
|
|
|
int err = nfs_refresh_inode(inode, fattr);
|
|
|
|
if (err < 0) {
|
|
|
|
iput(inode);
|
|
|
|
inode = ERR_PTR(err);
|
|
|
|
goto out_no_inode;
|
|
|
|
}
|
|
|
|
}
|
2013-12-17 10:20:16 -07:00
|
|
|
dprintk("NFS: nfs_fhget(%s/%Lu fh_crc=0x%08x ct=%d)\n",
|
2005-04-16 15:20:36 -07:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 10:20:16 -07:00
|
|
|
(unsigned long long)NFS_FILEID(inode),
|
2012-03-06 19:58:20 -07:00
|
|
|
nfs_display_fhandle_hash(fh),
|
2005-04-16 15:20:36 -07:00
|
|
|
atomic_read(&inode->i_count));
|
|
|
|
|
|
|
|
out:
|
|
|
|
return inode;
|
|
|
|
|
|
|
|
out_no_inode:
|
2006-03-20 11:44:48 -07:00
|
|
|
dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode));
|
2005-04-16 15:20:36 -07:00
|
|
|
goto out;
|
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_fhget);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2012-01-17 20:04:26 -07: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-16 15:20:36 -07:00
|
|
|
|
|
|
|
int
|
2023-01-13 04:49:11 -07:00
|
|
|
nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
2021-01-21 06:19:43 -07:00
|
|
|
struct iattr *attr)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2015-03-17 15:25:59 -07:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2010-04-16 13:22:52 -07:00
|
|
|
struct nfs_fattr *fattr;
|
2015-08-26 06:10:55 -07:00
|
|
|
int error = 0;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2006-03-20 11:44:14 -07:00
|
|
|
nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
|
|
|
|
|
2007-10-18 03:05:21 -07: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-16 15:20:36 -07:00
|
|
|
if (attr->ia_valid & ATTR_SIZE) {
|
2014-09-07 08:36:40 -07:00
|
|
|
BUG_ON(!S_ISREG(inode->i_mode));
|
|
|
|
|
2015-08-26 06:10:55 -07:00
|
|
|
error = inode_newsize_ok(inode, attr->ia_size);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (attr->ia_size == i_size_read(inode))
|
2005-04-16 15:20:36 -07:00
|
|
|
attr->ia_valid &= ~ATTR_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Optimization: if the end result is no change, don't RPC */
|
2021-04-12 17:08:00 -07:00
|
|
|
if (((attr->ia_valid & NFS_VALID_ATTRS) & ~(ATTR_FILE|ATTR_OPEN)) == 0)
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
|
|
|
|
2013-08-19 15:59:33 -07:00
|
|
|
trace_nfs_setattr_enter(inode);
|
|
|
|
|
2006-03-20 11:44:06 -07:00
|
|
|
/* Write all dirty data */
|
2015-03-25 13:38:33 -07:00
|
|
|
if (S_ISREG(inode->i_mode))
|
|
|
|
nfs_sync_inode(inode);
|
2010-04-16 13:22:52 -07:00
|
|
|
|
2021-10-22 10:11:08 -07:00
|
|
|
fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode));
|
2015-08-26 06:10:55 -07:00
|
|
|
if (fattr == NULL) {
|
|
|
|
error = -ENOMEM;
|
2010-04-16 13:22:52 -07:00
|
|
|
goto out;
|
2015-08-26 06:10:55 -07:00
|
|
|
}
|
|
|
|
|
2010-04-16 13:22:52 -07:00
|
|
|
error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
|
2005-08-16 08:49:44 -07:00
|
|
|
if (error == 0)
|
2013-05-22 09:50:44 -07:00
|
|
|
error = nfs_refresh_inode(inode, fattr);
|
2010-04-16 13:22:52 -07:00
|
|
|
nfs_free_fattr(fattr);
|
|
|
|
out:
|
2013-08-19 15:59:33 -07:00
|
|
|
trace_nfs_setattr_exit(inode, error);
|
2005-08-16 08:49:44 -07:00
|
|
|
return error;
|
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_setattr);
|
2005-08-16 08:49:44 -07:00
|
|
|
|
2008-06-11 09:21:19 -07: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-26 14:09:04 -07:00
|
|
|
* Note: must be called with inode->i_lock held!
|
2008-06-11 09:21:19 -07:00
|
|
|
*/
|
|
|
|
static int nfs_vmtruncate(struct inode * inode, loff_t offset)
|
|
|
|
{
|
2009-08-20 09:35:06 -07:00
|
|
|
int err;
|
2008-06-11 09:21:19 -07:00
|
|
|
|
2009-08-20 09:35:06 -07:00
|
|
|
err = inode_newsize_ok(inode, offset);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2008-06-11 09:21:19 -07:00
|
|
|
|
2021-10-04 07:10:16 -07:00
|
|
|
trace_nfs_size_truncate(inode, offset);
|
2009-08-20 09:35:06 -07:00
|
|
|
i_size_write(inode, offset);
|
2014-06-20 10:11:01 -07:00
|
|
|
/* Optimisation */
|
2023-03-21 15:27:04 -07:00
|
|
|
if (offset == 0) {
|
|
|
|
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA;
|
|
|
|
nfs_ooo_clear(NFS_I(inode));
|
|
|
|
}
|
2018-03-27 14:10:42 -07:00
|
|
|
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
|
2009-08-20 09:35:06 -07:00
|
|
|
|
2015-02-26 14:09:04 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2013-09-12 15:13:56 -07:00
|
|
|
truncate_pagecache(inode, offset);
|
2015-02-26 14:09:04 -07:00
|
|
|
spin_lock(&inode->i_lock);
|
2009-08-20 09:35:06 -07:00
|
|
|
out:
|
|
|
|
return err;
|
2008-06-11 09:21:19 -07:00
|
|
|
}
|
|
|
|
|
2005-08-16 08:49:44 -07:00
|
|
|
/**
|
|
|
|
* nfs_setattr_update_inode - Update inode metadata after a setattr call.
|
|
|
|
* @inode: pointer to struct inode
|
|
|
|
* @attr: pointer to struct iattr
|
2018-03-20 13:53:31 -07:00
|
|
|
* @fattr: pointer to struct nfs_fattr
|
2005-08-16 08:49:44 -07:00
|
|
|
*
|
|
|
|
* Note: we do this in the *proc.c in order to ensure that
|
|
|
|
* it works for things like exclusive creates too.
|
|
|
|
*/
|
2015-02-26 14:09:04 -07:00
|
|
|
void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
|
|
|
|
struct nfs_fattr *fattr)
|
2005-08-16 08:49:44 -07:00
|
|
|
{
|
2015-02-26 14:09:04 -07:00
|
|
|
/* Barrier: bump the attribute generation count. */
|
|
|
|
nfs_fattr_set_barrier(fattr);
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
NFS_I(inode)->attr_gencount = fattr->gencount;
|
2018-04-08 14:51:11 -07:00
|
|
|
if ((attr->ia_valid & ATTR_SIZE) != 0) {
|
2021-03-24 09:29:41 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME |
|
|
|
|
NFS_INO_INVALID_BLOCKS);
|
2018-04-08 14:51:11 -07:00
|
|
|
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
|
|
|
|
nfs_vmtruncate(inode, attr->ia_size);
|
|
|
|
}
|
2005-08-16 08:49:44 -07:00
|
|
|
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
|
2018-04-08 14:51:11 -07:00
|
|
|
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_CTIME;
|
2021-04-12 17:08:00 -07:00
|
|
|
if ((attr->ia_valid & ATTR_KILL_SUID) != 0 &&
|
|
|
|
inode->i_mode & S_ISUID)
|
|
|
|
inode->i_mode &= ~S_ISUID;
|
2023-03-14 04:51:10 -07:00
|
|
|
if (setattr_should_drop_sgid(&nop_mnt_idmap, inode))
|
2021-04-12 17:08:00 -07:00
|
|
|
inode->i_mode &= ~S_ISGID;
|
2005-04-16 15:20:36 -07:00
|
|
|
if ((attr->ia_valid & ATTR_MODE) != 0) {
|
2005-08-16 08:49:44 -07:00
|
|
|
int mode = attr->ia_mode & S_IALLUGO;
|
|
|
|
mode |= inode->i_mode & ~S_IALLUGO;
|
2005-04-16 15:20:36 -07: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;
|
2018-04-08 14:51:11 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
2023-07-05 12:01:22 -07:00
|
|
|
inode_set_ctime_to_ts(inode, fattr->ctime);
|
2018-04-08 14:51:11 -07:00
|
|
|
else
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE
|
|
|
|
| NFS_INO_INVALID_CTIME);
|
2014-06-20 10:11:01 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ACCESS
|
|
|
|
| NFS_INO_INVALID_ACL);
|
2005-08-16 08:49:44 -07:00
|
|
|
}
|
2018-04-08 14:51:11 -07:00
|
|
|
if (attr->ia_valid & (ATTR_ATIME_SET|ATTR_ATIME)) {
|
|
|
|
NFS_I(inode)->cache_validity &= ~(NFS_INO_INVALID_ATIME
|
|
|
|
| NFS_INO_INVALID_CTIME);
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_atime_to_ts(inode, fattr->atime);
|
2018-04-08 14:51:11 -07:00
|
|
|
else if (attr->ia_valid & ATTR_ATIME_SET)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_atime_to_ts(inode, attr->ia_atime);
|
2018-04-08 14:51:11 -07:00
|
|
|
else
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
|
|
|
|
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
2023-07-05 12:01:22 -07:00
|
|
|
inode_set_ctime_to_ts(inode, fattr->ctime);
|
2018-04-08 14:51:11 -07:00
|
|
|
else
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE
|
|
|
|
| NFS_INO_INVALID_CTIME);
|
|
|
|
}
|
|
|
|
if (attr->ia_valid & (ATTR_MTIME_SET|ATTR_MTIME)) {
|
|
|
|
NFS_I(inode)->cache_validity &= ~(NFS_INO_INVALID_MTIME
|
|
|
|
| NFS_INO_INVALID_CTIME);
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MTIME)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_mtime_to_ts(inode, fattr->mtime);
|
2018-04-08 14:51:11 -07:00
|
|
|
else if (attr->ia_valid & ATTR_MTIME_SET)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_mtime_to_ts(inode, attr->ia_mtime);
|
2018-04-08 14:51:11 -07:00
|
|
|
else
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
|
|
|
|
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
2023-07-05 12:01:22 -07:00
|
|
|
inode_set_ctime_to_ts(inode, fattr->ctime);
|
2018-04-08 14:51:11 -07:00
|
|
|
else
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE
|
|
|
|
| NFS_INO_INVALID_CTIME);
|
2005-08-16 08:49:44 -07:00
|
|
|
}
|
2015-11-25 11:50:45 -07:00
|
|
|
if (fattr->valid)
|
|
|
|
nfs_update_inode(inode, fattr);
|
2015-02-26 14:09:04 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2022-02-17 13:46:23 -07:00
|
|
|
/*
|
|
|
|
* Don't request help from readdirplus if the file is being written to,
|
|
|
|
* or if attribute caching is turned off
|
|
|
|
*/
|
|
|
|
static bool nfs_getattr_readdirplus_enable(const struct inode *inode)
|
2014-02-07 15:02:08 -07:00
|
|
|
{
|
2022-02-17 13:46:23 -07:00
|
|
|
return nfs_server_capable(inode, NFS_CAP_READDIRPLUS) &&
|
|
|
|
!nfs_have_writebacks(inode) && NFS_MAXATTRTIMEO(inode) > 5 * HZ;
|
|
|
|
}
|
2014-02-07 15:02:08 -07:00
|
|
|
|
2022-02-17 13:46:23 -07:00
|
|
|
static void nfs_readdirplus_parent_cache_miss(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
if (!IS_ROOT(dentry)) {
|
|
|
|
struct dentry *parent = dget_parent(dentry);
|
|
|
|
nfs_readdir_record_entry_cache_miss(d_inode(parent));
|
|
|
|
dput(parent);
|
|
|
|
}
|
2014-02-07 15:02:08 -07:00
|
|
|
}
|
|
|
|
|
2016-12-02 07:15:37 -07:00
|
|
|
static void nfs_readdirplus_parent_cache_hit(struct dentry *dentry)
|
|
|
|
{
|
2022-02-17 13:46:23 -07:00
|
|
|
if (!IS_ROOT(dentry)) {
|
|
|
|
struct dentry *parent = dget_parent(dentry);
|
|
|
|
nfs_readdir_record_entry_cache_hit(d_inode(parent));
|
|
|
|
dput(parent);
|
|
|
|
}
|
2016-12-02 07:15:37 -07:00
|
|
|
}
|
|
|
|
|
2021-03-24 11:29:12 -07:00
|
|
|
static u32 nfs_get_valid_attrmask(struct inode *inode)
|
2014-02-07 15:02:08 -07:00
|
|
|
{
|
2021-03-24 11:29:12 -07:00
|
|
|
unsigned long cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
|
|
|
|
u32 reply_mask = STATX_INO | STATX_TYPE;
|
|
|
|
|
|
|
|
if (!(cache_validity & NFS_INO_INVALID_ATIME))
|
|
|
|
reply_mask |= STATX_ATIME;
|
2021-03-25 18:07:21 -07:00
|
|
|
if (!(cache_validity & NFS_INO_INVALID_CTIME))
|
|
|
|
reply_mask |= STATX_CTIME;
|
|
|
|
if (!(cache_validity & NFS_INO_INVALID_MTIME))
|
|
|
|
reply_mask |= STATX_MTIME;
|
|
|
|
if (!(cache_validity & NFS_INO_INVALID_SIZE))
|
|
|
|
reply_mask |= STATX_SIZE;
|
2021-03-25 10:14:42 -07:00
|
|
|
if (!(cache_validity & NFS_INO_INVALID_NLINK))
|
|
|
|
reply_mask |= STATX_NLINK;
|
2021-04-13 06:41:16 -07:00
|
|
|
if (!(cache_validity & NFS_INO_INVALID_MODE))
|
|
|
|
reply_mask |= STATX_MODE;
|
2021-03-24 11:29:12 -07:00
|
|
|
if (!(cache_validity & NFS_INO_INVALID_OTHER))
|
2021-04-13 06:41:16 -07:00
|
|
|
reply_mask |= STATX_UID | STATX_GID;
|
2021-03-24 11:29:12 -07:00
|
|
|
if (!(cache_validity & NFS_INO_INVALID_BLOCKS))
|
|
|
|
reply_mask |= STATX_BLOCKS;
|
2022-08-05 10:47:00 -07:00
|
|
|
if (!(cache_validity & NFS_INO_INVALID_CHANGE))
|
|
|
|
reply_mask |= STATX_CHANGE_COOKIE;
|
2021-03-24 11:29:12 -07:00
|
|
|
return reply_mask;
|
2014-02-07 15:02:08 -07:00
|
|
|
}
|
|
|
|
|
2023-01-13 04:49:12 -07:00
|
|
|
int nfs_getattr(struct mnt_idmap *idmap, const struct path *path,
|
2021-01-21 06:19:43 -07:00
|
|
|
struct kstat *stat, u32 request_mask, unsigned int query_flags)
|
2005-04-16 15:20:36 -07: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 09:46:22 -07:00
|
|
|
struct inode *inode = d_inode(path->dentry);
|
2018-01-04 15:46:09 -07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
|
|
|
unsigned long cache_validity;
|
2014-10-23 05:02:47 -07:00
|
|
|
int err = 0;
|
2018-01-04 15:46:09 -07:00
|
|
|
bool force_sync = query_flags & AT_STATX_FORCE_SYNC;
|
|
|
|
bool do_update = false;
|
2022-02-17 13:46:23 -07:00
|
|
|
bool readdirplus_enabled = nfs_getattr_readdirplus_enable(inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-08-19 15:59:33 -07:00
|
|
|
trace_nfs_getattr_enter(inode);
|
2018-01-04 15:46:09 -07:00
|
|
|
|
2021-03-25 12:59:53 -07:00
|
|
|
request_mask &= STATX_TYPE | STATX_MODE | STATX_NLINK | STATX_UID |
|
|
|
|
STATX_GID | STATX_ATIME | STATX_MTIME | STATX_CTIME |
|
2023-06-12 06:34:04 -07:00
|
|
|
STATX_INO | STATX_SIZE | STATX_BLOCKS |
|
2022-08-05 10:47:00 -07:00
|
|
|
STATX_CHANGE_COOKIE;
|
2021-03-25 12:59:53 -07:00
|
|
|
|
2020-06-04 13:20:06 -07:00
|
|
|
if ((query_flags & AT_STATX_DONT_SYNC) && !force_sync) {
|
2022-02-17 13:46:23 -07:00
|
|
|
if (readdirplus_enabled)
|
|
|
|
nfs_readdirplus_parent_cache_hit(path->dentry);
|
2021-03-24 11:29:12 -07:00
|
|
|
goto out_no_revalidate;
|
2020-06-04 13:20:06 -07:00
|
|
|
}
|
2018-01-04 15:46:09 -07:00
|
|
|
|
2022-08-05 10:47:00 -07:00
|
|
|
/* Flush out writes to the server in order to update c/mtime/version. */
|
|
|
|
if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_CHANGE_COOKIE)) &&
|
2022-02-15 16:05:18 -07:00
|
|
|
S_ISREG(inode->i_mode))
|
|
|
|
filemap_write_and_wait(inode->i_mapping);
|
2006-01-09 21:52:17 -07: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
|
2017-11-27 14:05:09 -07:00
|
|
|
* - NFS never sets SB_NOATIME or SB_NODIRATIME so there is
|
2006-01-09 21:52:17 -07:00
|
|
|
* 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 09:46:22 -07:00
|
|
|
if ((path->mnt->mnt_flags & MNT_NOATIME) ||
|
|
|
|
((path->mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
|
2018-01-04 15:46:09 -07:00
|
|
|
request_mask &= ~STATX_ATIME;
|
|
|
|
|
|
|
|
/* Is the user requesting attributes that might need revalidation? */
|
|
|
|
if (!(request_mask & (STATX_MODE|STATX_NLINK|STATX_ATIME|STATX_CTIME|
|
|
|
|
STATX_MTIME|STATX_UID|STATX_GID|
|
2022-08-05 10:47:00 -07:00
|
|
|
STATX_SIZE|STATX_BLOCKS|
|
|
|
|
STATX_CHANGE_COOKIE)))
|
2018-01-04 15:46:09 -07:00
|
|
|
goto out_no_revalidate;
|
|
|
|
|
|
|
|
/* Check whether the cached attributes are stale */
|
|
|
|
do_update |= force_sync || nfs_attribute_cache_expired(inode);
|
|
|
|
cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
|
2021-03-24 10:55:09 -07:00
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_CHANGE;
|
2018-01-04 15:46:09 -07:00
|
|
|
if (request_mask & STATX_ATIME)
|
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_ATIME;
|
2021-03-24 10:55:09 -07:00
|
|
|
if (request_mask & STATX_CTIME)
|
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_CTIME;
|
|
|
|
if (request_mask & STATX_MTIME)
|
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_MTIME;
|
|
|
|
if (request_mask & STATX_SIZE)
|
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_SIZE;
|
2021-03-25 10:14:42 -07:00
|
|
|
if (request_mask & STATX_NLINK)
|
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_NLINK;
|
2021-04-13 06:41:16 -07:00
|
|
|
if (request_mask & STATX_MODE)
|
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_MODE;
|
|
|
|
if (request_mask & (STATX_UID | STATX_GID))
|
2021-03-24 10:55:09 -07:00
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_OTHER;
|
nfs: set invalid blocks after NFSv4 writes
Use the following command to test nfsv4(size of file1M is 1MB):
mount -t nfs -o vers=4.0,actimeo=60 127.0.0.1/dir1 /mnt
cp file1M /mnt
du -h /mnt/file1M -->0 within 60s, then 1M
When write is done(cp file1M /mnt), will call this:
nfs_writeback_done
nfs4_write_done
nfs4_write_done_cb
nfs_writeback_update_inode
nfs_post_op_update_inode_force_wcc_locked(change, ctime, mtime
nfs_post_op_update_inode_force_wcc_locked
nfs_set_cache_invalid
nfs_refresh_inode_locked
nfs_update_inode
nfsd write response contains change, ctime, mtime, the flag will be
clear after nfs_update_inode. Howerver, write response does not contain
space_used, previous open response contains space_used whose value is 0,
so inode->i_blocks is still 0.
nfs_getattr -->called by "du -h"
do_update |= force_sync || nfs_attribute_cache_expired -->false in 60s
cache_validity = READ_ONCE(NFS_I(inode)->cache_validity)
do_update |= cache_validity & (NFS_INO_INVALID_ATTR -->false
if (do_update) {
__nfs_revalidate_inode
}
Within 60s, does not send getattr request to nfsd, thus "du -h /mnt/file1M"
is 0.
Add a NFS_INO_INVALID_BLOCKS flag, set it when nfsv4 write is done.
Fixes: 16e143751727 ("NFS: More fine grained attribute tracking")
Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
2020-05-21 02:17:21 -07:00
|
|
|
if (request_mask & STATX_BLOCKS)
|
|
|
|
do_update |= cache_validity & NFS_INO_INVALID_BLOCKS;
|
2021-03-24 11:29:12 -07:00
|
|
|
|
2018-01-04 15:46:09 -07:00
|
|
|
if (do_update) {
|
2022-02-17 13:46:23 -07:00
|
|
|
if (readdirplus_enabled)
|
2017-04-28 03:35:19 -07:00
|
|
|
nfs_readdirplus_parent_cache_miss(path->dentry);
|
2014-02-07 15:02:08 -07:00
|
|
|
err = __nfs_revalidate_inode(server, inode);
|
2018-01-04 15:46:09 -07:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2022-02-17 13:46:23 -07:00
|
|
|
} else if (readdirplus_enabled)
|
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 09:46:22 -07:00
|
|
|
nfs_readdirplus_parent_cache_hit(path->dentry);
|
2018-01-04 15:46:09 -07:00
|
|
|
out_no_revalidate:
|
|
|
|
/* Only return attributes that were revalidated. */
|
2021-03-24 11:29:12 -07:00
|
|
|
stat->result_mask = nfs_get_valid_attrmask(inode) | request_mask;
|
|
|
|
|
2023-08-07 12:38:33 -07:00
|
|
|
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
|
2018-01-04 15:46:09 -07:00
|
|
|
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
|
2022-08-05 10:47:00 -07:00
|
|
|
stat->change_cookie = inode_peek_iversion_raw(inode);
|
|
|
|
stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC;
|
|
|
|
if (server->change_attr_type != NFS4_CHANGE_TYPE_IS_UNDEFINED)
|
|
|
|
stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC;
|
2018-01-04 15:46:09 -07:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
stat->blksize = NFS_SERVER(inode)->dtsize;
|
2010-02-19 18:03:26 -07:00
|
|
|
out:
|
2013-08-19 15:59:33 -07:00
|
|
|
trace_nfs_getattr_exit(inode, err);
|
2005-04-16 15:20:36 -07:00
|
|
|
return err;
|
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_getattr);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2010-06-25 13:35:53 -07:00
|
|
|
static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
|
|
|
|
{
|
2017-10-20 02:53:37 -07:00
|
|
|
refcount_set(&l_ctx->count, 1);
|
2016-10-12 21:26:47 -07:00
|
|
|
l_ctx->lockowner = current->files;
|
2010-06-25 13:35:53 -07:00
|
|
|
INIT_LIST_HEAD(&l_ctx->list);
|
2016-01-06 08:40:18 -07:00
|
|
|
atomic_set(&l_ctx->io_count, 0);
|
2010-06-25 13:35:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
|
|
|
|
{
|
2018-09-02 12:11:57 -07:00
|
|
|
struct nfs_lock_context *pos;
|
2010-06-25 13:35:53 -07:00
|
|
|
|
2018-09-02 12:11:57 -07:00
|
|
|
list_for_each_entry_rcu(pos, &ctx->lock_context.list, list) {
|
2016-10-12 21:26:47 -07:00
|
|
|
if (pos->lockowner != current->files)
|
2010-06-25 13:35:53 -07:00
|
|
|
continue;
|
2018-09-02 12:11:57 -07:00
|
|
|
if (refcount_inc_not_zero(&pos->count))
|
|
|
|
return pos;
|
|
|
|
}
|
2010-06-25 13:35:53 -07: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-17 15:25:59 -07:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2010-06-25 13:35:53 -07:00
|
|
|
|
2018-09-02 12:11:57 -07:00
|
|
|
rcu_read_lock();
|
2010-06-25 13:35:53 -07:00
|
|
|
res = __nfs_find_lock_context(ctx);
|
2018-09-02 12:11:57 -07:00
|
|
|
rcu_read_unlock();
|
2010-06-25 13:35:53 -07:00
|
|
|
if (res == NULL) {
|
2022-01-29 11:04:10 -07:00
|
|
|
new = kmalloc(sizeof(*new), GFP_KERNEL_ACCOUNT);
|
2010-06-25 13:35:53 -07:00
|
|
|
if (new == NULL)
|
2012-08-13 14:15:50 -07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2010-06-25 13:35:53 -07:00
|
|
|
nfs_init_lock_context(new);
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
res = __nfs_find_lock_context(ctx);
|
|
|
|
if (res == NULL) {
|
2019-04-07 10:59:10 -07:00
|
|
|
new->open_context = get_nfs_open_context(ctx);
|
|
|
|
if (new->open_context) {
|
|
|
|
list_add_tail_rcu(&new->list,
|
|
|
|
&ctx->lock_context.list);
|
|
|
|
res = new;
|
|
|
|
new = NULL;
|
|
|
|
} else
|
|
|
|
res = ERR_PTR(-EBADF);
|
2010-06-25 13:35:53 -07:00
|
|
|
}
|
2018-09-02 12:11:57 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
kfree(new);
|
2010-06-25 13:35:53 -07:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2014-09-26 10:58:48 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_get_lock_context);
|
2010-06-25 13:35:53 -07:00
|
|
|
|
|
|
|
void nfs_put_lock_context(struct nfs_lock_context *l_ctx)
|
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx = l_ctx->open_context;
|
2015-03-17 15:25:59 -07:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2010-06-25 13:35:53 -07:00
|
|
|
|
2017-10-20 02:53:37 -07:00
|
|
|
if (!refcount_dec_and_lock(&l_ctx->count, &inode->i_lock))
|
2010-06-25 13:35:53 -07:00
|
|
|
return;
|
2018-09-02 12:11:57 -07:00
|
|
|
list_del_rcu(&l_ctx->list);
|
2010-06-25 13:35:53 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2019-04-07 10:59:10 -07:00
|
|
|
put_nfs_open_context(ctx);
|
2018-09-02 12:11:57 -07:00
|
|
|
kfree_rcu(l_ctx, rcu_head);
|
2010-06-25 13:35:53 -07:00
|
|
|
}
|
2014-09-26 10:58:48 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_put_lock_context);
|
2010-06-25 13:35:53 -07:00
|
|
|
|
2009-03-19 12:35:50 -07:00
|
|
|
/**
|
|
|
|
* nfs_close_context - Common close_context() routine NFSv2/v3
|
|
|
|
* @ctx: pointer to context
|
|
|
|
* @is_sync: is this a synchronous close
|
|
|
|
*
|
2015-09-04 12:07:37 -07: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 12:35:50 -07:00
|
|
|
*/
|
|
|
|
void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
|
|
|
|
{
|
2015-09-04 12:07:37 -07:00
|
|
|
struct nfs_inode *nfsi;
|
2009-03-19 12:35:50 -07:00
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
if (!(ctx->mode & FMODE_WRITE))
|
|
|
|
return;
|
|
|
|
if (!is_sync)
|
|
|
|
return;
|
2015-03-17 15:25:59 -07:00
|
|
|
inode = d_inode(ctx->dentry);
|
2016-12-16 15:39:58 -07:00
|
|
|
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
|
|
|
|
return;
|
2015-09-04 12:07:37 -07: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 12:35:50 -07:00
|
|
|
return;
|
2021-03-25 08:04:34 -07:00
|
|
|
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOCTO)
|
2009-03-19 12:35:50 -07:00
|
|
|
return;
|
2021-03-25 08:04:34 -07:00
|
|
|
nfs_revalidate_inode(inode,
|
|
|
|
NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_SIZE);
|
2009-03-19 12:35:50 -07:00
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_close_context);
|
2009-03-19 12:35:50 -07:00
|
|
|
|
2016-10-12 21:26:47 -07:00
|
|
|
struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
|
|
|
|
fmode_t f_mode,
|
|
|
|
struct file *filp)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
|
2022-01-29 11:04:10 -07:00
|
|
|
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
|
2020-02-07 17:32:49 -07:00
|
|
|
if (!ctx)
|
2011-10-23 15:49:54 -07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
nfs_sb_active(dentry->d_sb);
|
|
|
|
ctx->dentry = dget(dentry);
|
2020-02-07 17:32:49 -07:00
|
|
|
if (filp)
|
|
|
|
ctx->cred = get_cred(filp->f_cred);
|
|
|
|
else
|
|
|
|
ctx->cred = get_current_cred();
|
2021-07-10 15:07:14 -07:00
|
|
|
rcu_assign_pointer(ctx->ll_cred, NULL);
|
2011-10-23 15:49:54 -07:00
|
|
|
ctx->state = NULL;
|
|
|
|
ctx->mode = f_mode;
|
|
|
|
ctx->flags = 0;
|
|
|
|
ctx->error = 0;
|
2016-10-12 21:26:47 -07:00
|
|
|
ctx->flock_owner = (fl_owner_t)filp;
|
2011-10-23 15:49:54 -07:00
|
|
|
nfs_init_lock_context(&ctx->lock_context);
|
|
|
|
ctx->lock_context.open_context = ctx;
|
|
|
|
INIT_LIST_HEAD(&ctx->list);
|
2012-05-23 02:02:35 -07:00
|
|
|
ctx->mdsthreshold = NULL;
|
2005-04-16 15:20:36 -07:00
|
|
|
return ctx;
|
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(alloc_nfs_open_context);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
|
|
|
|
{
|
2018-09-02 12:57:01 -07:00
|
|
|
if (ctx != NULL && refcount_inc_not_zero(&ctx->lock_context.count))
|
|
|
|
return ctx;
|
|
|
|
return NULL;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(get_nfs_open_context);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2009-03-19 12:35:50 -07:00
|
|
|
static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2015-03-17 15:25:59 -07:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2011-06-22 15:40:12 -07:00
|
|
|
struct super_block *sb = ctx->dentry->d_sb;
|
2007-06-17 13:02:44 -07:00
|
|
|
|
2018-09-02 12:57:01 -07:00
|
|
|
if (!refcount_dec_and_test(&ctx->lock_context.count))
|
|
|
|
return;
|
2010-09-27 12:51:20 -07:00
|
|
|
if (!list_empty(&ctx->list)) {
|
2018-09-02 12:57:01 -07:00
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
list_del_rcu(&ctx->list);
|
2010-09-23 09:22:09 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2018-09-02 12:57:01 -07:00
|
|
|
}
|
2010-09-27 12:51:20 -07:00
|
|
|
if (inode != NULL)
|
|
|
|
NFS_PROTO(inode)->close_context(ctx, is_sync);
|
2018-12-02 17:30:31 -07:00
|
|
|
put_cred(ctx->cred);
|
2011-06-22 15:40:12 -07:00
|
|
|
dput(ctx->dentry);
|
2013-01-11 14:39:51 -07:00
|
|
|
nfs_sb_deactive(sb);
|
2021-07-10 15:07:14 -07:00
|
|
|
put_rpccred(rcu_dereference_protected(ctx->ll_cred, 1));
|
2012-05-23 02:02:35 -07:00
|
|
|
kfree(ctx->mdsthreshold);
|
2018-09-02 12:57:01 -07:00
|
|
|
kfree_rcu(ctx, rcu_head);
|
2007-06-17 13:02:44 -07:00
|
|
|
}
|
|
|
|
|
2007-10-18 15:03:27 -07:00
|
|
|
void put_nfs_open_context(struct nfs_open_context *ctx)
|
|
|
|
{
|
|
|
|
__put_nfs_open_context(ctx, 0);
|
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(put_nfs_open_context);
|
2007-10-18 15:03:27 -07:00
|
|
|
|
2015-09-04 12:17:53 -07:00
|
|
|
static void put_nfs_open_context_sync(struct nfs_open_context *ctx)
|
|
|
|
{
|
|
|
|
__put_nfs_open_context(ctx, 1);
|
|
|
|
}
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* Ensure that mmap has a recent RPC credential for use when writing out
|
|
|
|
* shared pages
|
|
|
|
*/
|
2013-05-29 10:34:46 -07:00
|
|
|
void nfs_inode_attach_open_context(struct nfs_open_context *ctx)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2015-03-17 15:25:59 -07:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2005-04-16 15:20:36 -07:00
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
2019-05-22 05:38:57 -07:00
|
|
|
if (list_empty(&nfsi->open_files) &&
|
2023-03-21 15:27:04 -07:00
|
|
|
nfs_ooo_test(nfsi))
|
2021-03-08 12:42:54 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA |
|
|
|
|
NFS_INO_REVAL_FORCED);
|
2018-09-02 12:57:01 -07:00
|
|
|
list_add_tail_rcu(&ctx->list, &nfsi->open_files);
|
2005-04-16 15:20:36 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
2013-05-29 10:34:46 -07: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);
|
2021-05-11 20:41:10 -07:00
|
|
|
set_bit(NFS_CONTEXT_FILE_OPEN, &ctx->flags);
|
2013-05-29 10:34:46 -07:00
|
|
|
if (list_empty(&ctx->list))
|
|
|
|
nfs_inode_attach_open_context(ctx);
|
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_file_set_open_context);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2005-11-04 13:33:38 -07:00
|
|
|
/*
|
|
|
|
* Given an inode, search for an open context with the desired characteristics
|
|
|
|
*/
|
2018-12-02 17:30:31 -07:00
|
|
|
struct nfs_open_context *nfs_find_open_context(struct inode *inode, const struct cred *cred, fmode_t mode)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
struct nfs_open_context *pos, *ctx = NULL;
|
|
|
|
|
2018-09-02 12:57:01 -07:00
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(pos, &nfsi->open_files, list) {
|
2020-01-26 15:31:15 -07:00
|
|
|
if (cred != NULL && cred_fscmp(pos->cred, cred) != 0)
|
2005-11-04 13:33:38 -07:00
|
|
|
continue;
|
2010-03-25 10:54:49 -07:00
|
|
|
if ((pos->mode & (FMODE_READ|FMODE_WRITE)) != mode)
|
|
|
|
continue;
|
2021-05-11 20:41:10 -07:00
|
|
|
if (!test_bit(NFS_CONTEXT_FILE_OPEN, &pos->flags))
|
|
|
|
continue;
|
2010-03-25 10:54:49 -07:00
|
|
|
ctx = get_nfs_open_context(pos);
|
2018-09-02 12:57:01 -07:00
|
|
|
if (ctx)
|
|
|
|
break;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2018-09-02 12:57:01 -07:00
|
|
|
rcu_read_unlock();
|
2005-04-16 15:20:36 -07:00
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
2015-07-13 11:01:33 -07:00
|
|
|
void nfs_file_clear_open_context(struct file *filp)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2007-08-10 14:44:32 -07:00
|
|
|
struct nfs_open_context *ctx = nfs_file_open_context(filp);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
if (ctx) {
|
2015-03-17 15:25:59 -07:00
|
|
|
struct inode *inode = d_inode(ctx->dentry);
|
2013-05-29 10:34:46 -07:00
|
|
|
|
2021-05-11 20:41:10 -07:00
|
|
|
clear_bit(NFS_CONTEXT_FILE_OPEN, &ctx->flags);
|
2015-12-05 00:57:31 -07: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-16 15:20:36 -07:00
|
|
|
filp->private_data = NULL;
|
2015-09-04 12:17:53 -07:00
|
|
|
put_nfs_open_context_sync(ctx);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These allocate and release file read/write context information.
|
|
|
|
*/
|
|
|
|
int nfs_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
|
2022-09-22 22:40:14 -07:00
|
|
|
ctx = alloc_nfs_open_context(file_dentry(filp),
|
|
|
|
flags_to_mode(filp->f_flags), filp);
|
2011-10-23 15:49:54 -07:00
|
|
|
if (IS_ERR(ctx))
|
|
|
|
return PTR_ERR(ctx);
|
2005-04-16 15:20:36 -07:00
|
|
|
nfs_file_set_open_context(filp, ctx);
|
|
|
|
put_nfs_open_context(ctx);
|
2013-09-27 03:20:03 -07:00
|
|
|
nfs_fscache_open_file(inode, filp);
|
2005-04-16 15:20:36 -07: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;
|
2010-04-16 13:22:49 -07:00
|
|
|
struct nfs_fattr *fattr = NULL;
|
2005-04-16 15:20:36 -07:00
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
2013-12-17 10:20:16 -07:00
|
|
|
dfprintk(PAGECACHE, "NFS: revalidating (%s/%Lu)\n",
|
|
|
|
inode->i_sb->s_id, (unsigned long long)NFS_FILEID(inode));
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-08-19 15:59:33 -07:00
|
|
|
trace_nfs_revalidate_inode_enter(inode);
|
|
|
|
|
2006-10-19 23:28:42 -07:00
|
|
|
if (is_bad_inode(inode))
|
2008-10-05 11:48:22 -07:00
|
|
|
goto out;
|
2005-04-16 15:20:36 -07:00
|
|
|
if (NFS_STALE(inode))
|
2005-08-18 11:24:11 -07:00
|
|
|
goto out;
|
2007-09-28 16:11:33 -07:00
|
|
|
|
2016-07-05 10:46:53 -07: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-16 13:22:49 -07:00
|
|
|
status = -ENOMEM;
|
2021-10-22 10:11:07 -07:00
|
|
|
fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode));
|
2010-04-16 13:22:49 -07:00
|
|
|
if (fattr == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2008-10-05 11:48:22 -07:00
|
|
|
nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
|
2013-05-22 09:50:43 -07:00
|
|
|
|
2021-10-22 10:11:07 -07:00
|
|
|
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
if (status != 0) {
|
2013-12-17 10:20:16 -07:00
|
|
|
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) getattr failed, error=%d\n",
|
2005-04-16 15:20:36 -07:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 10:20:16 -07:00
|
|
|
(unsigned long long)NFS_FILEID(inode), status);
|
2020-01-06 13:39:37 -07:00
|
|
|
switch (status) {
|
|
|
|
case -ETIMEDOUT:
|
|
|
|
/* A soft timeout occurred. Use cached information? */
|
|
|
|
if (server->flags & NFS_MOUNT_SOFTREVAL)
|
|
|
|
status = 0;
|
|
|
|
break;
|
|
|
|
case -ESTALE:
|
2005-04-16 15:20:36 -07:00
|
|
|
if (!S_ISDIR(inode->i_mode))
|
2020-04-06 10:39:29 -07:00
|
|
|
nfs_set_inode_stale(inode);
|
|
|
|
else
|
|
|
|
nfs_zap_caches(inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2021-10-22 10:11:07 -07:00
|
|
|
goto out;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
2010-04-16 13:22:49 -07:00
|
|
|
status = nfs_refresh_inode(inode, fattr);
|
2005-04-16 15:20:36 -07:00
|
|
|
if (status) {
|
2013-12-17 10:20:16 -07:00
|
|
|
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) refresh failed, error=%d\n",
|
2005-04-16 15:20:36 -07:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 10:20:16 -07:00
|
|
|
(unsigned long long)NFS_FILEID(inode), status);
|
2021-10-22 10:11:07 -07:00
|
|
|
goto out;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2005-08-18 11:24:09 -07:00
|
|
|
|
2005-12-03 13:20:07 -07:00
|
|
|
if (nfsi->cache_validity & NFS_INO_INVALID_ACL)
|
2005-06-22 10:16:22 -07:00
|
|
|
nfs_zap_acl_cache(inode);
|
2005-08-18 11:24:09 -07:00
|
|
|
|
2021-10-22 10:11:12 -07:00
|
|
|
nfs_setsecurity(inode, fattr);
|
2013-11-02 03:57:18 -07:00
|
|
|
|
2013-12-17 10:20:16 -07:00
|
|
|
dfprintk(PAGECACHE, "NFS: (%s/%Lu) revalidation complete\n",
|
2005-04-16 15:20:36 -07:00
|
|
|
inode->i_sb->s_id,
|
2013-12-17 10:20:16 -07:00
|
|
|
(unsigned long long)NFS_FILEID(inode));
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-05-22 09:50:43 -07:00
|
|
|
out:
|
2010-04-16 13:22:49 -07:00
|
|
|
nfs_free_fattr(fattr);
|
2013-08-19 15:59:33 -07:00
|
|
|
trace_nfs_revalidate_inode_exit(inode, status);
|
2005-04-16 15:20:36 -07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2013-07-05 14:49:30 -07:00
|
|
|
int nfs_attribute_cache_expired(struct inode *inode)
|
2010-04-16 13:42:46 -07:00
|
|
|
{
|
2010-03-10 13:21:44 -07:00
|
|
|
if (nfs_have_delegated_attributes(inode))
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
2010-04-16 13:42:46 -07:00
|
|
|
return nfs_attribute_timeout(inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs_revalidate_inode - Revalidate the inode attributes
|
2019-02-18 11:32:38 -07:00
|
|
|
* @inode: pointer to inode struct
|
2021-03-25 08:04:34 -07:00
|
|
|
* @flags: cache flags to check
|
2005-04-16 15:20:36 -07:00
|
|
|
*
|
|
|
|
* Updates inode attribute information by retrieving the data from the server.
|
|
|
|
*/
|
2021-03-25 08:04:34 -07:00
|
|
|
int nfs_revalidate_inode(struct inode *inode, unsigned long flags)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2021-03-25 08:04:34 -07:00
|
|
|
if (!nfs_check_cache_invalid(inode, flags))
|
2005-04-16 15:20:36 -07:00
|
|
|
return NFS_STALE(inode) ? -ESTALE : 0;
|
2021-03-25 08:04:34 -07:00
|
|
|
return __nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2012-07-30 13:05:24 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2010-02-19 18:03:30 -07:00
|
|
|
static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
|
2007-01-24 12:54:55 -07:00
|
|
|
{
|
2013-08-05 10:26:31 -07:00
|
|
|
int ret;
|
|
|
|
|
nfs: Convert to new fscache volume/cookie API
Change the nfs filesystem to support fscache's indexing rewrite and
reenable caching in nfs.
The following changes have been made:
(1) The fscache_netfs struct is no more, and there's no need to register
the filesystem as a whole.
(2) The session cookie is now an fscache_volume cookie, allocated with
fscache_acquire_volume(). That takes three parameters: a string
representing the "volume" in the index, a string naming the cache to
use (or NULL) and a u64 that conveys coherency metadata for the
volume.
For nfs, I've made it render the volume name string as:
"nfs,<ver>,<family>,<address>,<port>,<fsidH>,<fsidL>*<,param>[,<uniq>]"
(3) The fscache_cookie_def is no more and needed information is passed
directly to fscache_acquire_cookie(). The cache no longer calls back
into the filesystem, but rather metadata changes are indicated at
other times.
fscache_acquire_cookie() is passed the same keying and coherency
information as before.
(4) fscache_enable/disable_cookie() have been removed.
Call fscache_use_cookie() and fscache_unuse_cookie() when a file is
opened or closed to prevent a cache file from being culled and to keep
resources to hand that are needed to do I/O.
If a file is opened for writing, we invalidate it with
FSCACHE_INVAL_DIO_WRITE in lieu of doing writeback to the cache,
thereby making it cease caching until all currently open files are
closed. This should give the same behaviour as the uptream code.
Making the cache store local modifications isn't straightforward for
NFS, so that's left for future patches.
(5) fscache_invalidate() now needs to be given uptodate auxiliary data and
a file size. It also takes a flag to indicate if this was due to a
DIO write.
(6) Call nfs_fscache_invalidate() with FSCACHE_INVAL_DIO_WRITE on a file
to which a DIO write is made.
(7) Call fscache_note_page_release() from nfs_release_page().
(8) Use a killable wait in nfs_vm_page_mkwrite() when waiting for
PG_fscache to be cleared.
(9) The functions to read and write data to/from the cache are stubbed out
pending a conversion to use netfslib.
Changes
=======
ver #3:
- Added missing =n fallback for nfs_fscache_release_file()[1][2].
ver #2:
- Use gfpflags_allow_blocking() rather than using flag directly.
- fscache_acquire_volume() now returns errors.
- Remove NFS_INO_FSCACHE as it's no longer used.
- Need to unuse a cookie on file-release, not inode-clear.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
Co-developed-by: David Howells <dhowells@redhat.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Dave Wysochanski <dwysocha@redhat.com>
Acked-by: Jeff Layton <jlayton@kernel.org>
cc: Trond Myklebust <trond.myklebust@hammerspace.com>
cc: Anna Schumaker <anna.schumaker@netapp.com>
cc: linux-nfs@vger.kernel.org
cc: linux-cachefs@redhat.com
Link: https://lore.kernel.org/r/202112100804.nksO8K4u-lkp@intel.com/ [1]
Link: https://lore.kernel.org/r/202112100957.2oEDT20W-lkp@intel.com/ [2]
Link: https://lore.kernel.org/r/163819668938.215744.14448852181937731615.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/163906979003.143852.2601189243864854724.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163967182112.1823006.7791504655391213379.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/164021575950.640689.12069642327533368467.stgit@warthog.procyon.org.uk/ # v4
2020-11-14 11:43:54 -07:00
|
|
|
nfs_fscache_invalidate(inode, 0);
|
2007-01-24 12:54:55 -07:00
|
|
|
if (mapping->nrpages != 0) {
|
2013-08-05 10:26:31 -07:00
|
|
|
if (S_ISREG(inode->i_mode)) {
|
|
|
|
ret = nfs_sync_mapping(mapping);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = invalidate_inode_pages2(mapping);
|
2007-01-24 12:54:55 -07:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
|
2013-08-19 15:59:33 -07:00
|
|
|
|
2013-12-17 10:20:16 -07:00
|
|
|
dfprintk(PAGECACHE, "NFS: (%s/%Lu) data cache invalidated\n",
|
|
|
|
inode->i_sb->s_id,
|
|
|
|
(unsigned long long)NFS_FILEID(inode));
|
2007-01-24 12:54:55 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-08 06:55:47 -07:00
|
|
|
* nfs_clear_invalid_mapping - Conditionally clear a mapping
|
2019-02-18 11:32:38 -07:00
|
|
|
* @mapping: pointer to mapping
|
2021-02-08 06:55:47 -07:00
|
|
|
*
|
|
|
|
* If the NFS_INO_INVALID_DATA inode flag is set, clear the mapping.
|
2007-01-24 12:54:55 -07:00
|
|
|
*/
|
2021-02-08 06:55:47 -07:00
|
|
|
int nfs_clear_invalid_mapping(struct address_space *mapping)
|
2007-01-24 12:54:55 -07:00
|
|
|
{
|
2021-02-08 06:55:47 -07:00
|
|
|
struct inode *inode = mapping->host;
|
2007-01-24 12:54:55 -07: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 11:46:15 -07:00
|
|
|
unsigned long *bitlock = &nfsi->flags;
|
2007-01-24 12:54:55 -07:00
|
|
|
int ret = 0;
|
2005-08-18 11:24:12 -07: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 11:46:15 -07: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-06 22:16:04 -07:00
|
|
|
ret = wait_on_bit_action(bitlock, NFS_INO_INVALIDATING,
|
freezer,sched: Rewrite core freezer logic
Rewrite the core freezer to behave better wrt thawing and be simpler
in general.
By replacing PF_FROZEN with TASK_FROZEN, a special block state, it is
ensured frozen tasks stay frozen until thawed and don't randomly wake
up early, as is currently possible.
As such, it does away with PF_FROZEN and PF_FREEZER_SKIP, freeing up
two PF_flags (yay!).
Specifically; the current scheme works a little like:
freezer_do_not_count();
schedule();
freezer_count();
And either the task is blocked, or it lands in try_to_freezer()
through freezer_count(). Now, when it is blocked, the freezer
considers it frozen and continues.
However, on thawing, once pm_freezing is cleared, freezer_count()
stops working, and any random/spurious wakeup will let a task run
before its time.
That is, thawing tries to thaw things in explicit order; kernel
threads and workqueues before doing bringing SMP back before userspace
etc.. However due to the above mentioned races it is entirely possible
for userspace tasks to thaw (by accident) before SMP is back.
This can be a fatal problem in asymmetric ISA architectures (eg ARMv9)
where the userspace task requires a special CPU to run.
As said; replace this with a special task state TASK_FROZEN and add
the following state transitions:
TASK_FREEZABLE -> TASK_FROZEN
__TASK_STOPPED -> TASK_FROZEN
__TASK_TRACED -> TASK_FROZEN
The new TASK_FREEZABLE can be set on any state part of TASK_NORMAL
(IOW. TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE) -- any such state
is already required to deal with spurious wakeups and the freezer
causes one such when thawing the task (since the original state is
lost).
The special __TASK_{STOPPED,TRACED} states *can* be restored since
their canonical state is in ->jobctl.
With this, frozen tasks need an explicit TASK_FROZEN wakeup and are
free of undue (early / spurious) wakeups.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/20220822114649.055452969@infradead.org
2022-08-22 04:18:22 -07:00
|
|
|
nfs_wait_bit_killable,
|
|
|
|
TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
|
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 11:46:15 -07:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2014-01-28 07:37:16 -07: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 11:46:15 -07:00
|
|
|
break;
|
|
|
|
spin_unlock(&inode->i_lock);
|
2014-01-28 07:37:16 -07:00
|
|
|
goto out;
|
2013-08-19 15:59:33 -07:00
|
|
|
}
|
|
|
|
|
2014-01-28 07:37:16 -07:00
|
|
|
set_bit(NFS_INO_INVALIDATING, bitlock);
|
2014-01-28 11:47:46 -07:00
|
|
|
smp_wmb();
|
2023-03-21 15:27:04 -07:00
|
|
|
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
|
|
|
|
nfs_ooo_clear(nfsi);
|
2014-01-28 07:37:16 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
trace_nfs_invalidate_mapping_enter(inode);
|
2016-06-22 05:19:36 -07:00
|
|
|
ret = nfs_invalidate_mapping(inode, mapping);
|
2014-01-28 07:37:16 -07: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 11:46:15 -07:00
|
|
|
clear_bit_unlock(NFS_INO_INVALIDATING, bitlock);
|
2014-03-17 10:06:10 -07: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 11:46:15 -07:00
|
|
|
wake_up_bit(bitlock, NFS_INO_INVALIDATING);
|
2006-10-19 23:28:40 -07:00
|
|
|
out:
|
2006-05-24 22:40:59 -07:00
|
|
|
return ret;
|
2005-06-22 10:16:30 -07:00
|
|
|
}
|
|
|
|
|
2021-02-08 06:55:47 -07:00
|
|
|
bool nfs_mapping_need_revalidate_inode(struct inode *inode)
|
|
|
|
{
|
2021-03-25 18:07:21 -07:00
|
|
|
return nfs_check_cache_invalid(inode, NFS_INO_INVALID_CHANGE) ||
|
2021-02-08 06:55:47 -07:00
|
|
|
NFS_STALE(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs_revalidate_mapping - Revalidate the pagecache
|
|
|
|
* @inode: pointer to host inode
|
|
|
|
* @mapping: pointer to mapping
|
|
|
|
*/
|
|
|
|
int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
|
|
|
|
{
|
|
|
|
/* swapfiles are not supposed to be shared. */
|
|
|
|
if (IS_SWAPFILE(inode))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (nfs_mapping_need_revalidate_inode(inode)) {
|
|
|
|
int ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nfs_clear_invalid_mapping(mapping);
|
|
|
|
}
|
|
|
|
|
2016-06-08 14:08:28 -07:00
|
|
|
static bool nfs_file_has_writers(struct nfs_inode *nfsi)
|
2015-03-02 21:32:08 -07:00
|
|
|
{
|
2016-06-08 14:08:28 -07:00
|
|
|
struct inode *inode = &nfsi->vfs_inode;
|
|
|
|
|
|
|
|
if (!S_ISREG(inode->i_mode))
|
|
|
|
return false;
|
|
|
|
if (list_empty(&nfsi->open_files))
|
|
|
|
return false;
|
2018-09-02 12:34:37 -07:00
|
|
|
return inode_is_open_for_write(inode);
|
2015-03-02 21:32:08 -07:00
|
|
|
}
|
|
|
|
|
2016-06-25 14:24:46 -07:00
|
|
|
static bool nfs_file_has_buffered_writers(struct nfs_inode *nfsi)
|
2015-03-02 21:32:08 -07:00
|
|
|
{
|
2016-06-25 14:24:46 -07:00
|
|
|
return nfs_file_has_writers(nfsi) && nfs_file_io_is_buffered(nfsi);
|
2015-03-02 21:32:08 -07:00
|
|
|
}
|
|
|
|
|
2018-03-20 13:53:29 -07:00
|
|
|
static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
2006-01-03 01:55:40 -07:00
|
|
|
{
|
2019-10-04 13:38:56 -07:00
|
|
|
struct timespec64 ts;
|
vfs: change inode times to use struct timespec64
struct timespec is not y2038 safe. Transition vfs to use
y2038 safe struct timespec64 instead.
The change was made with the help of the following cocinelle
script. This catches about 80% of the changes.
All the header file and logic changes are included in the
first 5 rules. The rest are trivial substitutions.
I avoid changing any of the function signatures or any other
filesystem specific data structures to keep the patch simple
for review.
The script can be a little shorter by combining different cases.
But, this version was sufficient for my usecase.
virtual patch
@ depends on patch @
identifier now;
@@
- struct timespec
+ struct timespec64
current_time ( ... )
{
- struct timespec now = current_kernel_time();
+ struct timespec64 now = current_kernel_time64();
...
- return timespec_trunc(
+ return timespec64_trunc(
... );
}
@ depends on patch @
identifier xtime;
@@
struct \( iattr \| inode \| kstat \) {
...
- struct timespec xtime;
+ struct timespec64 xtime;
...
}
@ depends on patch @
identifier t;
@@
struct inode_operations {
...
int (*update_time) (...,
- struct timespec t,
+ struct timespec64 t,
...);
...
}
@ depends on patch @
identifier t;
identifier fn_update_time =~ "update_time$";
@@
fn_update_time (...,
- struct timespec *t,
+ struct timespec64 *t,
...) { ... }
@ depends on patch @
identifier t;
@@
lease_get_mtime( ... ,
- struct timespec *t
+ struct timespec64 *t
) { ... }
@te depends on patch forall@
identifier ts;
local idexpression struct inode *inode_node;
identifier i_xtime =~ "^i_[acm]time$";
identifier ia_xtime =~ "^ia_[acm]time$";
identifier fn_update_time =~ "update_time$";
identifier fn;
expression e, E3;
local idexpression struct inode *node1;
local idexpression struct inode *node2;
local idexpression struct iattr *attr1;
local idexpression struct iattr *attr2;
local idexpression struct iattr attr;
identifier i_xtime1 =~ "^i_[acm]time$";
identifier i_xtime2 =~ "^i_[acm]time$";
identifier ia_xtime1 =~ "^ia_[acm]time$";
identifier ia_xtime2 =~ "^ia_[acm]time$";
@@
(
(
- struct timespec ts;
+ struct timespec64 ts;
|
- struct timespec ts = current_time(inode_node);
+ struct timespec64 ts = current_time(inode_node);
)
<+... when != ts
(
- timespec_equal(&inode_node->i_xtime, &ts)
+ timespec64_equal(&inode_node->i_xtime, &ts)
|
- timespec_equal(&ts, &inode_node->i_xtime)
+ timespec64_equal(&ts, &inode_node->i_xtime)
|
- timespec_compare(&inode_node->i_xtime, &ts)
+ timespec64_compare(&inode_node->i_xtime, &ts)
|
- timespec_compare(&ts, &inode_node->i_xtime)
+ timespec64_compare(&ts, &inode_node->i_xtime)
|
ts = current_time(e)
|
fn_update_time(..., &ts,...)
|
inode_node->i_xtime = ts
|
node1->i_xtime = ts
|
ts = inode_node->i_xtime
|
<+... attr1->ia_xtime ...+> = ts
|
ts = attr1->ia_xtime
|
ts.tv_sec
|
ts.tv_nsec
|
btrfs_set_stack_timespec_sec(..., ts.tv_sec)
|
btrfs_set_stack_timespec_nsec(..., ts.tv_nsec)
|
- ts = timespec64_to_timespec(
+ ts =
...
-)
|
- ts = ktime_to_timespec(
+ ts = ktime_to_timespec64(
...)
|
- ts = E3
+ ts = timespec_to_timespec64(E3)
|
- ktime_get_real_ts(&ts)
+ ktime_get_real_ts64(&ts)
|
fn(...,
- ts
+ timespec64_to_timespec(ts)
,...)
)
...+>
(
<... when != ts
- return ts;
+ return timespec64_to_timespec(ts);
...>
)
|
- timespec_equal(&node1->i_xtime1, &node2->i_xtime2)
+ timespec64_equal(&node1->i_xtime2, &node2->i_xtime2)
|
- timespec_equal(&node1->i_xtime1, &attr2->ia_xtime2)
+ timespec64_equal(&node1->i_xtime2, &attr2->ia_xtime2)
|
- timespec_compare(&node1->i_xtime1, &node2->i_xtime2)
+ timespec64_compare(&node1->i_xtime1, &node2->i_xtime2)
|
node1->i_xtime1 =
- timespec_trunc(attr1->ia_xtime1,
+ timespec64_trunc(attr1->ia_xtime1,
...)
|
- attr1->ia_xtime1 = timespec_trunc(attr2->ia_xtime2,
+ attr1->ia_xtime1 = timespec64_trunc(attr2->ia_xtime2,
...)
|
- ktime_get_real_ts(&attr1->ia_xtime1)
+ ktime_get_real_ts64(&attr1->ia_xtime1)
|
- ktime_get_real_ts(&attr.ia_xtime1)
+ ktime_get_real_ts64(&attr.ia_xtime1)
)
@ depends on patch @
struct inode *node;
struct iattr *attr;
identifier fn;
identifier i_xtime =~ "^i_[acm]time$";
identifier ia_xtime =~ "^ia_[acm]time$";
expression e;
@@
(
- fn(node->i_xtime);
+ fn(timespec64_to_timespec(node->i_xtime));
|
fn(...,
- node->i_xtime);
+ timespec64_to_timespec(node->i_xtime));
|
- e = fn(attr->ia_xtime);
+ e = fn(timespec64_to_timespec(attr->ia_xtime));
)
@ depends on patch forall @
struct inode *node;
struct iattr *attr;
identifier i_xtime =~ "^i_[acm]time$";
identifier ia_xtime =~ "^ia_[acm]time$";
identifier fn;
@@
{
+ struct timespec ts;
<+...
(
+ ts = timespec64_to_timespec(node->i_xtime);
fn (...,
- &node->i_xtime,
+ &ts,
...);
|
+ ts = timespec64_to_timespec(attr->ia_xtime);
fn (...,
- &attr->ia_xtime,
+ &ts,
...);
)
...+>
}
@ depends on patch forall @
struct inode *node;
struct iattr *attr;
struct kstat *stat;
identifier ia_xtime =~ "^ia_[acm]time$";
identifier i_xtime =~ "^i_[acm]time$";
identifier xtime =~ "^[acm]time$";
identifier fn, ret;
@@
{
+ struct timespec ts;
<+...
(
+ ts = timespec64_to_timespec(node->i_xtime);
ret = fn (...,
- &node->i_xtime,
+ &ts,
...);
|
+ ts = timespec64_to_timespec(node->i_xtime);
ret = fn (...,
- &node->i_xtime);
+ &ts);
|
+ ts = timespec64_to_timespec(attr->ia_xtime);
ret = fn (...,
- &attr->ia_xtime,
+ &ts,
...);
|
+ ts = timespec64_to_timespec(attr->ia_xtime);
ret = fn (...,
- &attr->ia_xtime);
+ &ts);
|
+ ts = timespec64_to_timespec(stat->xtime);
ret = fn (...,
- &stat->xtime);
+ &ts);
)
...+>
}
@ depends on patch @
struct inode *node;
struct inode *node2;
identifier i_xtime1 =~ "^i_[acm]time$";
identifier i_xtime2 =~ "^i_[acm]time$";
identifier i_xtime3 =~ "^i_[acm]time$";
struct iattr *attrp;
struct iattr *attrp2;
struct iattr attr ;
identifier ia_xtime1 =~ "^ia_[acm]time$";
identifier ia_xtime2 =~ "^ia_[acm]time$";
struct kstat *stat;
struct kstat stat1;
struct timespec64 ts;
identifier xtime =~ "^[acmb]time$";
expression e;
@@
(
( node->i_xtime2 \| attrp->ia_xtime2 \| attr.ia_xtime2 \) = node->i_xtime1 ;
|
node->i_xtime2 = \( node2->i_xtime1 \| timespec64_trunc(...) \);
|
node->i_xtime2 = node->i_xtime1 = node->i_xtime3 = \(ts \| current_time(...) \);
|
node->i_xtime1 = node->i_xtime3 = \(ts \| current_time(...) \);
|
stat->xtime = node2->i_xtime1;
|
stat1.xtime = node2->i_xtime1;
|
( node->i_xtime2 \| attrp->ia_xtime2 \) = attrp->ia_xtime1 ;
|
( attrp->ia_xtime1 \| attr.ia_xtime1 \) = attrp2->ia_xtime2;
|
- e = node->i_xtime1;
+ e = timespec64_to_timespec( node->i_xtime1 );
|
- e = attrp->ia_xtime1;
+ e = timespec64_to_timespec( attrp->ia_xtime1 );
|
node->i_xtime1 = current_time(...);
|
node->i_xtime2 = node->i_xtime1 = node->i_xtime3 =
- e;
+ timespec_to_timespec64(e);
|
node->i_xtime1 = node->i_xtime3 =
- e;
+ timespec_to_timespec64(e);
|
- node->i_xtime1 = e;
+ node->i_xtime1 = timespec_to_timespec64(e);
)
Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
Cc: <anton@tuxera.com>
Cc: <balbi@kernel.org>
Cc: <bfields@fieldses.org>
Cc: <darrick.wong@oracle.com>
Cc: <dhowells@redhat.com>
Cc: <dsterba@suse.com>
Cc: <dwmw2@infradead.org>
Cc: <hch@lst.de>
Cc: <hirofumi@mail.parknet.co.jp>
Cc: <hubcap@omnibond.com>
Cc: <jack@suse.com>
Cc: <jaegeuk@kernel.org>
Cc: <jaharkes@cs.cmu.edu>
Cc: <jslaby@suse.com>
Cc: <keescook@chromium.org>
Cc: <mark@fasheh.com>
Cc: <miklos@szeredi.hu>
Cc: <nico@linaro.org>
Cc: <reiserfs-devel@vger.kernel.org>
Cc: <richard@nod.at>
Cc: <sage@redhat.com>
Cc: <sfrench@samba.org>
Cc: <swhiteho@redhat.com>
Cc: <tj@kernel.org>
Cc: <trond.myklebust@primarydata.com>
Cc: <tytso@mit.edu>
Cc: <viro@zeniv.linux.org.uk>
2018-05-08 19:36:02 -07:00
|
|
|
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE)
|
|
|
|
&& (fattr->valid & NFS_ATTR_FATTR_CHANGE)
|
2018-02-01 06:15:25 -07:00
|
|
|
&& inode_eq_iversion_raw(inode, fattr->pre_change_attr)) {
|
2018-01-09 06:21:17 -07:00
|
|
|
inode_set_iversion_raw(inode, fattr->change_attr);
|
2007-09-30 12:21:24 -07:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
2014-06-20 10:11:01 -07:00
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
|
2020-06-23 15:39:00 -07:00
|
|
|
else if (nfs_server_capable(inode, NFS_CAP_XATTR))
|
|
|
|
nfs_set_cache_invalid(inode, NFS_INO_INVALID_XATTR);
|
2007-09-30 12:21:24 -07:00
|
|
|
}
|
2006-01-03 01:55:40 -07:00
|
|
|
/* If we have atomic WCC data, we may update some attributes */
|
2023-07-05 12:01:22 -07:00
|
|
|
ts = inode_get_ctime(inode);
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME)
|
|
|
|
&& (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
2019-10-04 13:38:56 -07:00
|
|
|
&& timespec64_equal(&ts, &fattr->pre_ctime)) {
|
2023-07-05 12:01:22 -07:00
|
|
|
inode_set_ctime_to_ts(inode, fattr->ctime);
|
2011-01-25 13:28:21 -07:00
|
|
|
}
|
2009-03-11 11:10:24 -07:00
|
|
|
|
2023-10-04 11:52:36 -07:00
|
|
|
ts = inode_get_mtime(inode);
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME)
|
|
|
|
&& (fattr->valid & NFS_ATTR_FATTR_MTIME)
|
2019-10-04 13:38:56 -07:00
|
|
|
&& timespec64_equal(&ts, &fattr->pre_mtime)) {
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_mtime_to_ts(inode, fattr->mtime);
|
2006-01-03 01:55:40 -07:00
|
|
|
}
|
2009-03-11 11:10:24 -07: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)
|
2017-08-01 12:39:46 -07:00
|
|
|
&& !nfs_have_writebacks(inode)) {
|
2021-10-04 07:10:16 -07:00
|
|
|
trace_nfs_size_wcc(inode, fattr->size);
|
2011-01-25 13:28:21 -07:00
|
|
|
i_size_write(inode, nfs_size_to_loff_t(fattr->size));
|
|
|
|
}
|
2006-01-03 01:55:40 -07:00
|
|
|
}
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/**
|
2005-10-27 19:12:39 -07:00
|
|
|
* nfs_check_inode_attributes - verify consistency of the inode attribute cache
|
2019-02-18 11:32:38 -07:00
|
|
|
* @inode: pointer to inode
|
|
|
|
* @fattr: updated attributes
|
2005-04-16 15:20:36 -07:00
|
|
|
*
|
|
|
|
* 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-27 19:12:39 -07:00
|
|
|
static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fattr)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
loff_t cur_size, new_isize;
|
2007-10-08 11:26:13 -07:00
|
|
|
unsigned long invalid = 0;
|
2019-10-04 13:38:56 -07:00
|
|
|
struct timespec64 ts;
|
2005-08-18 11:24:12 -07:00
|
|
|
|
2018-06-03 09:12:52 -07:00
|
|
|
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
|
2012-04-29 09:30:19 -07:00
|
|
|
return 0;
|
2018-06-03 09:12:52 -07:00
|
|
|
|
2019-08-28 08:26:13 -07:00
|
|
|
if (!(fattr->valid & NFS_ATTR_FATTR_FILEID)) {
|
|
|
|
/* Only a mounted-on-fileid? Just exit */
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
|
|
|
|
return 0;
|
2006-03-20 11:44:07 -07:00
|
|
|
/* Has the inode gone and changed behind our back? */
|
2019-08-28 08:26:13 -07:00
|
|
|
} else if (nfsi->fileid != fattr->fileid) {
|
2019-08-09 09:06:43 -07:00
|
|
|
/* Is this perhaps the mounted-on fileid? */
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) &&
|
|
|
|
nfsi->fileid == fattr->mounted_on_fileid)
|
|
|
|
return 0;
|
2017-07-04 19:22:20 -07:00
|
|
|
return -ESTALE;
|
2019-08-09 09:06:43 -07:00
|
|
|
}
|
2021-03-01 18:37:10 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode))
|
2017-07-04 19:22:20 -07:00
|
|
|
return -ESTALE;
|
2006-03-20 11:44:07 -07:00
|
|
|
|
2019-08-09 09:06:43 -07:00
|
|
|
|
2016-06-25 14:24:46 -07:00
|
|
|
if (!nfs_file_has_buffered_writers(nfsi)) {
|
2016-06-08 14:08:28 -07:00
|
|
|
/* Verify a few of the more important attributes */
|
2018-02-01 06:15:25 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && !inode_eq_iversion_raw(inode, fattr->change_attr))
|
2021-03-25 15:13:36 -07:00
|
|
|
invalid |= NFS_INO_INVALID_CHANGE;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2023-10-04 11:52:36 -07:00
|
|
|
ts = inode_get_mtime(inode);
|
2019-10-04 13:38:56 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec64_equal(&ts, &fattr->mtime))
|
2018-03-20 13:53:31 -07:00
|
|
|
invalid |= NFS_INO_INVALID_MTIME;
|
2006-03-20 11:44:07 -07:00
|
|
|
|
2023-07-05 12:01:22 -07:00
|
|
|
ts = inode_get_ctime(inode);
|
2019-10-04 13:38:56 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CTIME) && !timespec64_equal(&ts, &fattr->ctime))
|
2018-03-20 13:53:31 -07:00
|
|
|
invalid |= NFS_INO_INVALID_CTIME;
|
2016-06-08 14:08:28 -07:00
|
|
|
|
|
|
|
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)
|
2021-03-25 15:13:36 -07:00
|
|
|
invalid |= NFS_INO_INVALID_SIZE;
|
2016-06-08 14:08:28 -07:00
|
|
|
}
|
2009-03-11 11:10:24 -07:00
|
|
|
}
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
/* Have any file permissions changed? */
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO))
|
2021-03-25 15:13:36 -07:00
|
|
|
invalid |= NFS_INO_INVALID_MODE;
|
2013-02-01 15:26:23 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && !uid_eq(inode->i_uid, fattr->uid))
|
2021-03-25 15:13:36 -07:00
|
|
|
invalid |= NFS_INO_INVALID_OTHER;
|
2013-02-01 15:26:23 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && !gid_eq(inode->i_gid, fattr->gid))
|
2021-03-25 15:13:36 -07:00
|
|
|
invalid |= NFS_INO_INVALID_OTHER;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
/* Has the link count changed? */
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_NLINK) && inode->i_nlink != fattr->nlink)
|
2021-03-25 10:14:42 -07:00
|
|
|
invalid |= NFS_INO_INVALID_NLINK;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2023-10-04 11:52:36 -07:00
|
|
|
ts = inode_get_atime(inode);
|
2019-10-04 13:38:56 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_ATIME) && !timespec64_equal(&ts, &fattr->atime))
|
2007-10-08 11:26:13 -07:00
|
|
|
invalid |= NFS_INO_INVALID_ATIME;
|
|
|
|
|
|
|
|
if (invalid != 0)
|
2018-06-03 09:12:52 -07:00
|
|
|
nfs_set_cache_invalid(inode, invalid);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2005-10-27 19:12:39 -07:00
|
|
|
nfsi->read_cache_jiffies = fattr->time_start;
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-28 12:21:40 -07:00
|
|
|
static atomic_long_t nfs_attr_generation_counter;
|
2008-10-14 16:16:07 -07:00
|
|
|
|
|
|
|
static unsigned long nfs_read_attr_generation_counter(void)
|
|
|
|
{
|
2008-10-28 12:21:40 -07:00
|
|
|
return atomic_long_read(&nfs_attr_generation_counter);
|
2008-10-14 16:16:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long nfs_inc_attr_generation_counter(void)
|
|
|
|
{
|
2008-10-28 12:21:40 -07:00
|
|
|
return atomic_long_inc_return(&nfs_attr_generation_counter);
|
2008-10-14 16:16:07 -07:00
|
|
|
}
|
2015-02-26 17:52:06 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_inc_attr_generation_counter);
|
2008-10-14 16:16:07 -07: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 11:22:46 -07:00
|
|
|
fattr->owner_name = NULL;
|
|
|
|
fattr->group_name = NULL;
|
2008-10-14 16:16:07 -07:00
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_fattr_init);
|
2008-10-14 16:16:07 -07:00
|
|
|
|
2015-02-26 15:42:42 -07: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-16 13:22:45 -07:00
|
|
|
struct nfs_fattr *nfs_alloc_fattr(void)
|
|
|
|
{
|
|
|
|
struct nfs_fattr *fattr;
|
|
|
|
|
2022-01-29 11:57:38 -07:00
|
|
|
fattr = kmalloc(sizeof(*fattr), GFP_KERNEL);
|
2021-11-04 15:03:26 -07:00
|
|
|
if (fattr != NULL) {
|
2010-04-16 13:22:45 -07:00
|
|
|
nfs_fattr_init(fattr);
|
2021-11-04 15:03:26 -07:00
|
|
|
fattr->label = NULL;
|
|
|
|
}
|
2010-04-16 13:22:45 -07:00
|
|
|
return fattr;
|
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_alloc_fattr);
|
2010-04-16 13:22:45 -07:00
|
|
|
|
2021-10-22 10:11:00 -07:00
|
|
|
struct nfs_fattr *nfs_alloc_fattr_with_label(struct nfs_server *server)
|
|
|
|
{
|
|
|
|
struct nfs_fattr *fattr = nfs_alloc_fattr();
|
|
|
|
|
|
|
|
if (!fattr)
|
|
|
|
return NULL;
|
|
|
|
|
2022-01-29 11:57:38 -07:00
|
|
|
fattr->label = nfs4_label_alloc(server, GFP_KERNEL);
|
2021-10-22 10:11:00 -07:00
|
|
|
if (IS_ERR(fattr->label)) {
|
|
|
|
kfree(fattr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fattr;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_alloc_fattr_with_label);
|
|
|
|
|
2010-04-16 13:22:45 -07:00
|
|
|
struct nfs_fh *nfs_alloc_fhandle(void)
|
|
|
|
{
|
|
|
|
struct nfs_fh *fh;
|
|
|
|
|
2022-01-29 11:57:38 -07:00
|
|
|
fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
|
2010-04-16 13:22:45 -07:00
|
|
|
if (fh != NULL)
|
|
|
|
fh->size = 0;
|
|
|
|
return fh;
|
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_alloc_fhandle);
|
2010-04-16 13:22:45 -07:00
|
|
|
|
2012-03-18 11:07:42 -07:00
|
|
|
#ifdef NFS_DEBUG
|
2012-03-06 18:46:43 -07: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-12 13:06:31 -07:00
|
|
|
return nfs_fhandle_hash(fh);
|
2012-03-06 18:46:43 -07:00
|
|
|
}
|
2013-10-17 11:12:45 -07:00
|
|
|
EXPORT_SYMBOL_GPL(_nfs_display_fhandle_hash);
|
2012-03-06 18:46:43 -07:00
|
|
|
|
|
|
|
/*
|
2012-03-01 15:01:31 -07: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 08:14:35 -07:00
|
|
|
if (fh == NULL || fh->size == 0) {
|
2012-03-01 15:01:31 -07:00
|
|
|
printk(KERN_DEFAULT "%s at %p is empty\n", caption, fh);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-06 18:46:43 -07: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-01 15:01:31 -07: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 11:12:45 -07:00
|
|
|
EXPORT_SYMBOL_GPL(_nfs_display_fhandle);
|
2012-03-01 15:01:31 -07:00
|
|
|
#endif
|
|
|
|
|
2008-09-23 14:28:41 -07:00
|
|
|
/**
|
2021-03-26 08:01:19 -07:00
|
|
|
* nfs_inode_attrs_cmp_generic - compare attributes
|
2019-02-18 11:32:38 -07:00
|
|
|
* @fattr: attributes
|
|
|
|
* @inode: pointer to inode
|
2008-09-23 14:28:41 -07:00
|
|
|
*
|
|
|
|
* Attempt to divine whether or not an RPC call reply carrying stale
|
|
|
|
* attributes got scheduled after another call carrying updated ones.
|
2008-10-14 16:16:07 -07:00
|
|
|
* Note also the check for wraparound of 'attr_gencount'
|
2008-09-23 14:28:41 -07:00
|
|
|
*
|
2021-03-26 08:01:19 -07:00
|
|
|
* The function returns '1' if it thinks the attributes in @fattr are
|
|
|
|
* more recent than the ones cached in @inode. Otherwise it returns
|
|
|
|
* the value '0'.
|
2008-09-23 14:28:41 -07:00
|
|
|
*/
|
2021-03-26 08:01:19 -07:00
|
|
|
static int nfs_inode_attrs_cmp_generic(const struct nfs_fattr *fattr,
|
|
|
|
const struct inode *inode)
|
2008-09-23 14:28:41 -07:00
|
|
|
{
|
2021-03-29 13:46:05 -07:00
|
|
|
unsigned long attr_gencount = NFS_I(inode)->attr_gencount;
|
2008-09-23 14:28:41 -07:00
|
|
|
|
2021-03-29 13:46:05 -07:00
|
|
|
return (long)(fattr->gencount - attr_gencount) > 0 ||
|
|
|
|
(long)(attr_gencount - nfs_read_attr_generation_counter()) > 0;
|
2008-09-23 14:28:41 -07:00
|
|
|
}
|
|
|
|
|
2021-03-26 08:01:19 -07:00
|
|
|
/**
|
|
|
|
* nfs_inode_attrs_cmp_monotonic - compare attributes
|
2019-02-18 11:32:38 -07:00
|
|
|
* @fattr: attributes
|
2021-03-26 08:01:19 -07:00
|
|
|
* @inode: pointer to inode
|
2008-09-23 14:28:41 -07:00
|
|
|
*
|
|
|
|
* Attempt to divine whether or not an RPC call reply carrying stale
|
|
|
|
* attributes got scheduled after another call carrying updated ones.
|
|
|
|
*
|
2021-03-26 08:01:19 -07:00
|
|
|
* We assume that the server observes monotonic semantics for
|
|
|
|
* the change attribute, so a larger value means that the attributes in
|
|
|
|
* @fattr are more recent, in which case the function returns the
|
|
|
|
* value '1'.
|
|
|
|
* A return value of '0' indicates no measurable change
|
|
|
|
* A return value of '-1' means that the attributes in @inode are
|
|
|
|
* more recent.
|
|
|
|
*/
|
|
|
|
static int nfs_inode_attrs_cmp_monotonic(const struct nfs_fattr *fattr,
|
|
|
|
const struct inode *inode)
|
2008-09-23 14:28:41 -07:00
|
|
|
{
|
2021-03-26 08:01:19 -07:00
|
|
|
s64 diff = fattr->change_attr - inode_peek_iversion_raw(inode);
|
|
|
|
if (diff > 0)
|
|
|
|
return 1;
|
|
|
|
return diff == 0 ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs_inode_attrs_cmp_strict_monotonic - compare attributes
|
|
|
|
* @fattr: attributes
|
|
|
|
* @inode: pointer to inode
|
2008-09-23 14:28:41 -07:00
|
|
|
*
|
2021-03-26 08:01:19 -07:00
|
|
|
* Attempt to divine whether or not an RPC call reply carrying stale
|
|
|
|
* attributes got scheduled after another call carrying updated ones.
|
2008-09-23 14:28:41 -07:00
|
|
|
*
|
2021-03-26 08:01:19 -07:00
|
|
|
* We assume that the server observes strictly monotonic semantics for
|
|
|
|
* the change attribute, so a larger value means that the attributes in
|
|
|
|
* @fattr are more recent, in which case the function returns the
|
|
|
|
* value '1'.
|
|
|
|
* A return value of '-1' means that the attributes in @inode are
|
|
|
|
* more recent or unchanged.
|
2008-09-23 14:28:41 -07:00
|
|
|
*/
|
2021-03-26 08:01:19 -07:00
|
|
|
static int nfs_inode_attrs_cmp_strict_monotonic(const struct nfs_fattr *fattr,
|
|
|
|
const struct inode *inode)
|
2008-09-23 14:28:41 -07:00
|
|
|
{
|
2021-03-26 08:01:19 -07:00
|
|
|
return nfs_inode_attrs_cmp_monotonic(fattr, inode) > 0 ? 1 : -1;
|
|
|
|
}
|
2008-09-23 14:28:41 -07:00
|
|
|
|
2021-03-26 08:01:19 -07:00
|
|
|
/**
|
|
|
|
* nfs_inode_attrs_cmp - compare attributes
|
|
|
|
* @fattr: attributes
|
|
|
|
* @inode: pointer to inode
|
|
|
|
*
|
|
|
|
* This function returns '1' if it thinks the attributes in @fattr are
|
|
|
|
* more recent than the ones cached in @inode. It returns '-1' if
|
|
|
|
* the attributes in @inode are more recent than the ones in @fattr,
|
|
|
|
* and it returns 0 if not sure.
|
|
|
|
*/
|
|
|
|
static int nfs_inode_attrs_cmp(const struct nfs_fattr *fattr,
|
|
|
|
const struct inode *inode)
|
|
|
|
{
|
|
|
|
if (nfs_inode_attrs_cmp_generic(fattr, inode) > 0)
|
|
|
|
return 1;
|
|
|
|
switch (NFS_SERVER(inode)->change_attr_type) {
|
|
|
|
case NFS4_CHANGE_TYPE_IS_UNDEFINED:
|
|
|
|
break;
|
|
|
|
case NFS4_CHANGE_TYPE_IS_TIME_METADATA:
|
|
|
|
if (!(fattr->valid & NFS_ATTR_FATTR_CHANGE))
|
|
|
|
break;
|
|
|
|
return nfs_inode_attrs_cmp_monotonic(fattr, inode);
|
|
|
|
default:
|
|
|
|
if (!(fattr->valid & NFS_ATTR_FATTR_CHANGE))
|
|
|
|
break;
|
|
|
|
return nfs_inode_attrs_cmp_strict_monotonic(fattr, inode);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-09 06:30:21 -07:00
|
|
|
/**
|
|
|
|
* nfs_inode_finish_partial_attr_update - complete a previous inode update
|
|
|
|
* @fattr: attributes
|
|
|
|
* @inode: pointer to inode
|
|
|
|
*
|
|
|
|
* Returns '1' if the last attribute update left the inode cached
|
|
|
|
* attributes in a partially unrevalidated state, and @fattr
|
|
|
|
* matches the change attribute of that partial update.
|
|
|
|
* Otherwise returns '0'.
|
|
|
|
*/
|
|
|
|
static int nfs_inode_finish_partial_attr_update(const struct nfs_fattr *fattr,
|
|
|
|
const struct inode *inode)
|
|
|
|
{
|
|
|
|
const unsigned long check_valid =
|
|
|
|
NFS_INO_INVALID_ATIME | NFS_INO_INVALID_CTIME |
|
|
|
|
NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
|
|
|
|
NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_OTHER |
|
|
|
|
NFS_INO_INVALID_NLINK;
|
|
|
|
unsigned long cache_validity = NFS_I(inode)->cache_validity;
|
2021-09-26 11:05:04 -07:00
|
|
|
enum nfs4_change_attr_type ctype = NFS_SERVER(inode)->change_attr_type;
|
2021-04-09 06:30:21 -07:00
|
|
|
|
2021-09-26 11:05:04 -07:00
|
|
|
if (ctype != NFS4_CHANGE_TYPE_IS_UNDEFINED &&
|
|
|
|
!(cache_validity & NFS_INO_INVALID_CHANGE) &&
|
2021-04-09 06:30:21 -07:00
|
|
|
(cache_validity & check_valid) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
|
|
|
|
nfs_inode_attrs_cmp_monotonic(fattr, inode) == 0)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
2008-09-23 14:28:41 -07:00
|
|
|
}
|
|
|
|
|
2023-03-21 15:27:04 -07:00
|
|
|
static void nfs_ooo_merge(struct nfs_inode *nfsi,
|
|
|
|
u64 start, u64 end)
|
|
|
|
{
|
|
|
|
int i, cnt;
|
|
|
|
|
|
|
|
if (nfsi->cache_validity & NFS_INO_DATA_INVAL_DEFER)
|
|
|
|
/* No point merging anything */
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!nfsi->ooo) {
|
|
|
|
nfsi->ooo = kmalloc(sizeof(*nfsi->ooo), GFP_ATOMIC);
|
|
|
|
if (!nfsi->ooo) {
|
|
|
|
nfsi->cache_validity |= NFS_INO_DATA_INVAL_DEFER;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nfsi->ooo->cnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add this range, merging if possible */
|
|
|
|
cnt = nfsi->ooo->cnt;
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
if (end == nfsi->ooo->gap[i].start)
|
|
|
|
end = nfsi->ooo->gap[i].end;
|
|
|
|
else if (start == nfsi->ooo->gap[i].end)
|
|
|
|
start = nfsi->ooo->gap[i].start;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
/* Remove 'i' from table and loop to insert the new range */
|
|
|
|
cnt -= 1;
|
|
|
|
nfsi->ooo->gap[i] = nfsi->ooo->gap[cnt];
|
|
|
|
i = -1;
|
|
|
|
}
|
|
|
|
if (start != end) {
|
|
|
|
if (cnt >= ARRAY_SIZE(nfsi->ooo->gap)) {
|
|
|
|
nfsi->cache_validity |= NFS_INO_DATA_INVAL_DEFER;
|
|
|
|
kfree(nfsi->ooo);
|
|
|
|
nfsi->ooo = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nfsi->ooo->gap[cnt].start = start;
|
|
|
|
nfsi->ooo->gap[cnt].end = end;
|
|
|
|
cnt += 1;
|
|
|
|
}
|
|
|
|
nfsi->ooo->cnt = cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs_ooo_record(struct nfs_inode *nfsi,
|
|
|
|
struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
/* This reply was out-of-order, so record in the
|
|
|
|
* pre/post change id, possibly cancelling
|
|
|
|
* gaps created when iversion was jumpped forward.
|
|
|
|
*/
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PRECHANGE))
|
|
|
|
nfs_ooo_merge(nfsi,
|
|
|
|
fattr->change_attr,
|
|
|
|
fattr->pre_change_attr);
|
|
|
|
}
|
|
|
|
|
2021-03-26 08:01:19 -07:00
|
|
|
static int nfs_refresh_inode_locked(struct inode *inode,
|
|
|
|
struct nfs_fattr *fattr)
|
2008-09-23 14:28:41 -07:00
|
|
|
{
|
2021-03-26 08:01:19 -07:00
|
|
|
int attr_cmp = nfs_inode_attrs_cmp(fattr, inode);
|
|
|
|
int ret = 0;
|
2013-08-19 15:59:33 -07:00
|
|
|
|
|
|
|
trace_nfs_refresh_inode_enter(inode);
|
|
|
|
|
2021-04-09 06:30:21 -07:00
|
|
|
if (attr_cmp > 0 || nfs_inode_finish_partial_attr_update(fattr, inode))
|
2013-08-19 15:59:33 -07:00
|
|
|
ret = nfs_update_inode(inode, fattr);
|
2023-03-21 15:27:04 -07:00
|
|
|
else {
|
|
|
|
nfs_ooo_record(NFS_I(inode), fattr);
|
|
|
|
|
|
|
|
if (attr_cmp == 0)
|
|
|
|
ret = nfs_check_inode_attributes(inode, fattr);
|
|
|
|
}
|
2013-08-19 15:59:33 -07:00
|
|
|
|
|
|
|
trace_nfs_refresh_inode_exit(inode, ret);
|
|
|
|
return ret;
|
2008-10-05 09:07:23 -07:00
|
|
|
}
|
|
|
|
|
2005-10-27 19:12:39 -07:00
|
|
|
/**
|
|
|
|
* nfs_refresh_inode - try to update the inode attribute cache
|
2019-02-18 11:32:38 -07:00
|
|
|
* @inode: pointer to inode
|
|
|
|
* @fattr: updated attributes
|
2005-10-27 19:12:39 -07:00
|
|
|
*
|
|
|
|
* 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 09:07:23 -07:00
|
|
|
status = nfs_refresh_inode_locked(inode, fattr);
|
2005-10-27 19:12:39 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2009-04-03 08:42:43 -07:00
|
|
|
|
2005-10-27 19:12:39 -07:00
|
|
|
return status;
|
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_refresh_inode);
|
2005-10-27 19:12:39 -07:00
|
|
|
|
2018-03-20 13:53:31 -07:00
|
|
|
static int nfs_post_op_update_inode_locked(struct inode *inode,
|
|
|
|
struct nfs_fattr *fattr, unsigned int invalid)
|
2008-10-05 09:27:55 -07:00
|
|
|
{
|
2014-06-20 10:11:01 -07:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
invalid |= NFS_INO_INVALID_DATA;
|
|
|
|
nfs_set_cache_invalid(inode, invalid);
|
2008-10-05 09:27:55 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
|
|
|
|
return 0;
|
|
|
|
return nfs_refresh_inode_locked(inode, fattr);
|
|
|
|
}
|
|
|
|
|
2005-10-27 19:12:39 -07:00
|
|
|
/**
|
|
|
|
* nfs_post_op_update_inode - try to update the inode attribute cache
|
2019-02-18 11:32:38 -07:00
|
|
|
* @inode: pointer to inode
|
|
|
|
* @fattr: updated attributes
|
2005-10-27 19:12:39 -07:00
|
|
|
*
|
|
|
|
* After an operation that has changed the inode metadata, mark the
|
|
|
|
* attribute cache as being invalid, then try to update it.
|
2006-09-20 11:33:04 -07: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-27 19:12:39 -07:00
|
|
|
*/
|
|
|
|
int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
|
|
|
{
|
2008-10-05 09:27:55 -07:00
|
|
|
int status;
|
2005-10-27 19:12:39 -07:00
|
|
|
|
2007-10-01 06:59:15 -07:00
|
|
|
spin_lock(&inode->i_lock);
|
2015-02-26 17:48:26 -07:00
|
|
|
nfs_fattr_set_barrier(fattr);
|
2018-03-20 13:53:31 -07:00
|
|
|
status = nfs_post_op_update_inode_locked(inode, fattr,
|
|
|
|
NFS_INO_INVALID_CHANGE
|
2018-05-29 08:15:49 -07:00
|
|
|
| NFS_INO_INVALID_CTIME
|
|
|
|
| NFS_INO_REVAL_FORCED);
|
2007-10-01 06:59:15 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2013-05-22 09:50:44 -07:00
|
|
|
|
2008-10-05 09:07:23 -07:00
|
|
|
return status;
|
2005-10-27 19:12:39 -07:00
|
|
|
}
|
2012-07-30 13:05:24 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
|
2005-10-27 19:12:39 -07:00
|
|
|
|
2007-09-30 12:21:24 -07:00
|
|
|
/**
|
2015-02-26 15:36:09 -07:00
|
|
|
* nfs_post_op_update_inode_force_wcc_locked - update the inode attribute cache
|
2019-02-18 11:32:38 -07:00
|
|
|
* @inode: pointer to inode
|
|
|
|
* @fattr: updated attributes
|
2007-09-30 12:21:24 -07:00
|
|
|
*
|
|
|
|
* 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-26 15:36:09 -07:00
|
|
|
int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr)
|
2007-09-30 12:21:24 -07:00
|
|
|
{
|
2021-03-26 08:01:19 -07:00
|
|
|
int attr_cmp = nfs_inode_attrs_cmp(fattr, inode);
|
2008-10-05 09:27:55 -07:00
|
|
|
int status;
|
|
|
|
|
|
|
|
/* Don't do a WCC update if these attributes are already stale */
|
2021-03-26 08:01:19 -07:00
|
|
|
if (attr_cmp < 0)
|
|
|
|
return 0;
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR) == 0 || !attr_cmp) {
|
2023-03-21 15:27:04 -07:00
|
|
|
/* Record the pre/post change info before clearing PRECHANGE */
|
|
|
|
nfs_ooo_record(NFS_I(inode), fattr);
|
2009-03-11 11:10:24 -07:00
|
|
|
fattr->valid &= ~(NFS_ATTR_FATTR_PRECHANGE
|
|
|
|
| NFS_ATTR_FATTR_PRESIZE
|
|
|
|
| NFS_ATTR_FATTR_PREMTIME
|
|
|
|
| NFS_ATTR_FATTR_PRECTIME);
|
2008-10-05 09:27:55 -07:00
|
|
|
goto out_noforce;
|
|
|
|
}
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PRECHANGE) == 0) {
|
2018-01-09 06:21:17 -07:00
|
|
|
fattr->pre_change_attr = inode_peek_iversion_raw(inode);
|
2009-03-11 11:10:24 -07:00
|
|
|
fattr->valid |= NFS_ATTR_FATTR_PRECHANGE;
|
2007-09-30 12:21:24 -07:00
|
|
|
}
|
2009-03-11 11:10:24 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_CTIME) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PRECTIME) == 0) {
|
2023-07-05 12:01:22 -07:00
|
|
|
fattr->pre_ctime = inode_get_ctime(inode);
|
2009-03-11 11:10:24 -07:00
|
|
|
fattr->valid |= NFS_ATTR_FATTR_PRECTIME;
|
|
|
|
}
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) != 0 &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_PREMTIME) == 0) {
|
2023-10-04 11:52:36 -07:00
|
|
|
fattr->pre_mtime = inode_get_mtime(inode);
|
2009-03-11 11:10:24 -07: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 09:21:19 -07:00
|
|
|
fattr->pre_size = i_size_read(inode);
|
2009-03-11 11:10:24 -07:00
|
|
|
fattr->valid |= NFS_ATTR_FATTR_PRESIZE;
|
2007-09-30 12:21:24 -07:00
|
|
|
}
|
2008-10-05 09:27:55 -07:00
|
|
|
out_noforce:
|
2018-03-20 13:53:31 -07:00
|
|
|
status = nfs_post_op_update_inode_locked(inode, fattr,
|
|
|
|
NFS_INO_INVALID_CHANGE
|
|
|
|
| NFS_INO_INVALID_CTIME
|
nfs: set invalid blocks after NFSv4 writes
Use the following command to test nfsv4(size of file1M is 1MB):
mount -t nfs -o vers=4.0,actimeo=60 127.0.0.1/dir1 /mnt
cp file1M /mnt
du -h /mnt/file1M -->0 within 60s, then 1M
When write is done(cp file1M /mnt), will call this:
nfs_writeback_done
nfs4_write_done
nfs4_write_done_cb
nfs_writeback_update_inode
nfs_post_op_update_inode_force_wcc_locked(change, ctime, mtime
nfs_post_op_update_inode_force_wcc_locked
nfs_set_cache_invalid
nfs_refresh_inode_locked
nfs_update_inode
nfsd write response contains change, ctime, mtime, the flag will be
clear after nfs_update_inode. Howerver, write response does not contain
space_used, previous open response contains space_used whose value is 0,
so inode->i_blocks is still 0.
nfs_getattr -->called by "du -h"
do_update |= force_sync || nfs_attribute_cache_expired -->false in 60s
cache_validity = READ_ONCE(NFS_I(inode)->cache_validity)
do_update |= cache_validity & (NFS_INO_INVALID_ATTR -->false
if (do_update) {
__nfs_revalidate_inode
}
Within 60s, does not send getattr request to nfsd, thus "du -h /mnt/file1M"
is 0.
Add a NFS_INO_INVALID_BLOCKS flag, set it when nfsv4 write is done.
Fixes: 16e143751727 ("NFS: More fine grained attribute tracking")
Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
2020-05-21 02:17:21 -07:00
|
|
|
| NFS_INO_INVALID_MTIME
|
|
|
|
| NFS_INO_INVALID_BLOCKS);
|
2015-02-26 15:36:09 -07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
|
2019-02-18 11:32:38 -07:00
|
|
|
* @inode: pointer to inode
|
|
|
|
* @fattr: updated attributes
|
2015-02-26 15:36:09 -07:00
|
|
|
*
|
|
|
|
* 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-26 15:54:58 -07:00
|
|
|
nfs_fattr_set_barrier(fattr);
|
2015-02-26 15:36:09 -07:00
|
|
|
status = nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
|
2008-10-05 09:27:55 -07:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
return status;
|
2007-09-30 12:21:24 -07:00
|
|
|
}
|
2012-07-30 13:05:23 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_post_op_update_inode_force_wcc);
|
2007-09-30 12:21:24 -07: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 11:35:59 -07:00
|
|
|
|
2005-04-16 15:20:36 -07: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 13:20:07 -07:00
|
|
|
static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2018-03-05 13:01:18 -07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2005-06-22 10:16:30 -07:00
|
|
|
loff_t cur_isize, new_isize;
|
2018-03-05 13:01:18 -07:00
|
|
|
u64 fattr_supported = server->fattr_valid;
|
2007-10-08 11:26:13 -07:00
|
|
|
unsigned long invalid = 0;
|
2007-02-05 15:26:28 -07:00
|
|
|
unsigned long now = jiffies;
|
2009-08-09 12:06:19 -07:00
|
|
|
unsigned long save_cache_validity;
|
2016-06-25 14:24:46 -07:00
|
|
|
bool have_writers = nfs_file_has_buffered_writers(nfsi);
|
2016-07-28 11:41:10 -07:00
|
|
|
bool cache_revalidated = true;
|
2018-06-03 08:56:05 -07:00
|
|
|
bool attr_changed = false;
|
2018-06-03 08:38:38 -07:00
|
|
|
bool have_delegation;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2013-12-17 10:20:16 -07:00
|
|
|
dfprintk(VFS, "NFS: %s(%s/%lu fh_crc=0x%08x ct=%d info=0x%x)\n",
|
2008-05-02 13:42:44 -07:00
|
|
|
__func__, inode->i_sb->s_id, inode->i_ino,
|
2012-03-06 19:58:20 -07:00
|
|
|
nfs_display_fhandle_hash(NFS_FH(inode)),
|
2005-04-16 15:20:36 -07:00
|
|
|
atomic_read(&inode->i_count), fattr->valid);
|
|
|
|
|
2019-08-28 08:26:13 -07:00
|
|
|
if (!(fattr->valid & NFS_ATTR_FATTR_FILEID)) {
|
|
|
|
/* Only a mounted-on-fileid? Just exit */
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
|
|
|
|
return 0;
|
2019-08-09 09:06:43 -07:00
|
|
|
/* Has the inode gone and changed behind our back? */
|
2019-08-28 08:26:13 -07:00
|
|
|
} else if (nfsi->fileid != fattr->fileid) {
|
2019-08-09 09:06:43 -07:00
|
|
|
/* Is this perhaps the mounted-on fileid? */
|
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) &&
|
|
|
|
nfsi->fileid == fattr->mounted_on_fileid)
|
|
|
|
return 0;
|
2012-04-30 08:32:57 -07: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-16 15:20:36 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the inode's type hasn't changed.
|
|
|
|
*/
|
2021-03-01 18:37:10 -07:00
|
|
|
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode)) {
|
2012-04-30 08:32:57 -07:00
|
|
|
/*
|
|
|
|
* Big trouble! The inode has become a different object.
|
|
|
|
*/
|
2013-12-17 10:20:16 -07:00
|
|
|
printk(KERN_DEBUG "NFS: %s: inode %lu mode changed, %07o to %07o\n",
|
2012-04-30 08:32:57 -07:00
|
|
|
__func__, inode->i_ino, inode->i_mode, fattr->mode);
|
|
|
|
goto out_err;
|
|
|
|
}
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2007-06-05 10:26:15 -07:00
|
|
|
/* Update the fsid? */
|
2009-03-11 11:10:24 -07:00
|
|
|
if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) &&
|
2008-03-06 10:34:50 -07:00
|
|
|
!nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
|
2011-01-14 11:45:42 -07:00
|
|
|
!IS_AUTOMOUNT(inode))
|
2006-06-09 06:34:19 -07:00
|
|
|
server->fsid = fattr->fsid;
|
|
|
|
|
2018-06-03 08:38:38 -07:00
|
|
|
/* Save the delegation state before clearing cache_validity */
|
|
|
|
have_delegation = nfs_have_delegated_attributes(inode);
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* Update the read time so we don't revalidate too often.
|
|
|
|
*/
|
2005-10-27 19:12:39 -07:00
|
|
|
nfsi->read_cache_jiffies = fattr->time_start;
|
2007-02-05 15:26:28 -07:00
|
|
|
|
2009-08-09 12:06:19 -07:00
|
|
|
save_cache_validity = nfsi->cache_validity;
|
|
|
|
nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
|
|
|
|
| NFS_INO_INVALID_ATIME
|
|
|
|
| NFS_INO_REVAL_FORCED
|
nfs: set invalid blocks after NFSv4 writes
Use the following command to test nfsv4(size of file1M is 1MB):
mount -t nfs -o vers=4.0,actimeo=60 127.0.0.1/dir1 /mnt
cp file1M /mnt
du -h /mnt/file1M -->0 within 60s, then 1M
When write is done(cp file1M /mnt), will call this:
nfs_writeback_done
nfs4_write_done
nfs4_write_done_cb
nfs_writeback_update_inode
nfs_post_op_update_inode_force_wcc_locked(change, ctime, mtime
nfs_post_op_update_inode_force_wcc_locked
nfs_set_cache_invalid
nfs_refresh_inode_locked
nfs_update_inode
nfsd write response contains change, ctime, mtime, the flag will be
clear after nfs_update_inode. Howerver, write response does not contain
space_used, previous open response contains space_used whose value is 0,
so inode->i_blocks is still 0.
nfs_getattr -->called by "du -h"
do_update |= force_sync || nfs_attribute_cache_expired -->false in 60s
cache_validity = READ_ONCE(NFS_I(inode)->cache_validity)
do_update |= cache_validity & (NFS_INO_INVALID_ATTR -->false
if (do_update) {
__nfs_revalidate_inode
}
Within 60s, does not send getattr request to nfsd, thus "du -h /mnt/file1M"
is 0.
Add a NFS_INO_INVALID_BLOCKS flag, set it when nfsv4 write is done.
Fixes: 16e143751727 ("NFS: More fine grained attribute tracking")
Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
2020-05-21 02:17:21 -07:00
|
|
|
| NFS_INO_INVALID_BLOCKS);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2006-01-03 01:55:40 -07:00
|
|
|
/* Do atomic weak cache consistency updates */
|
2018-03-20 13:53:29 -07:00
|
|
|
nfs_wcc_update_inode(inode, fattr);
|
2006-01-03 01:55:40 -07:00
|
|
|
|
2016-07-28 11:41:10 -07:00
|
|
|
if (pnfs_layoutcommit_outstanding(inode)) {
|
2021-03-25 11:51:36 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity &
|
|
|
|
(NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
|
|
|
|
NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
|
2021-04-11 11:31:24 -07:00
|
|
|
NFS_INO_INVALID_BLOCKS);
|
2016-07-28 11:41:10 -07:00
|
|
|
cache_revalidated = false;
|
|
|
|
}
|
2016-07-17 21:51:01 -07:00
|
|
|
|
2007-09-27 12:57:24 -07:00
|
|
|
/* More cache consistency checks */
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CHANGE) {
|
2023-03-21 15:27:04 -07:00
|
|
|
if (!have_writers && nfsi->ooo && nfsi->ooo->cnt == 1 &&
|
|
|
|
nfsi->ooo->gap[0].end == inode_peek_iversion_raw(inode)) {
|
|
|
|
/* There is one remaining gap that hasn't been
|
|
|
|
* merged into iversion - do that now.
|
|
|
|
*/
|
|
|
|
inode_set_iversion_raw(inode, nfsi->ooo->gap[0].start);
|
|
|
|
kfree(nfsi->ooo);
|
|
|
|
nfsi->ooo = NULL;
|
|
|
|
}
|
2018-02-01 06:15:25 -07:00
|
|
|
if (!inode_eq_iversion_raw(inode, fattr->change_attr)) {
|
2016-06-07 18:44:08 -07:00
|
|
|
/* Could it be a race with writeback? */
|
2018-06-03 08:38:38 -07:00
|
|
|
if (!(have_writers || have_delegation)) {
|
2018-06-03 08:56:05 -07:00
|
|
|
invalid |= NFS_INO_INVALID_DATA
|
2016-06-07 18:44:08 -07:00
|
|
|
| NFS_INO_INVALID_ACCESS
|
2020-06-23 15:39:04 -07:00
|
|
|
| NFS_INO_INVALID_ACL
|
|
|
|
| NFS_INO_INVALID_XATTR;
|
2018-03-20 13:53:30 -07:00
|
|
|
/* Force revalidate of all attributes */
|
2018-03-20 13:53:31 -07:00
|
|
|
save_cache_validity |= NFS_INO_INVALID_CTIME
|
|
|
|
| NFS_INO_INVALID_MTIME
|
|
|
|
| NFS_INO_INVALID_SIZE
|
2021-03-24 09:29:41 -07:00
|
|
|
| NFS_INO_INVALID_BLOCKS
|
2021-03-25 10:14:42 -07:00
|
|
|
| NFS_INO_INVALID_NLINK
|
2021-04-13 06:41:16 -07:00
|
|
|
| NFS_INO_INVALID_MODE
|
2018-03-20 13:53:31 -07:00
|
|
|
| NFS_INO_INVALID_OTHER;
|
2016-06-07 18:44:08 -07:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
nfs_force_lookup_revalidate(inode);
|
2021-06-26 08:07:59 -07:00
|
|
|
attr_changed = true;
|
2018-06-03 08:38:38 -07:00
|
|
|
dprintk("NFS: change_attr change on server for file %s/%ld\n",
|
|
|
|
inode->i_sb->s_id,
|
|
|
|
inode->i_ino);
|
2023-03-21 15:27:04 -07:00
|
|
|
} else if (!have_delegation) {
|
|
|
|
nfs_ooo_record(nfsi, fattr);
|
|
|
|
nfs_ooo_merge(nfsi, inode_peek_iversion_raw(inode),
|
|
|
|
fattr->change_attr);
|
|
|
|
}
|
2018-01-09 06:21:17 -07:00
|
|
|
inode_set_iversion_raw(inode, fattr->change_attr);
|
2009-03-11 11:10:24 -07:00
|
|
|
}
|
2015-12-29 16:55:19 -07:00
|
|
|
} else {
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_CHANGE;
|
2021-06-26 06:23:51 -07:00
|
|
|
if (!have_delegation ||
|
|
|
|
(nfsi->cache_validity & NFS_INO_INVALID_CHANGE) != 0)
|
|
|
|
cache_revalidated = false;
|
2015-12-29 16:55:19 -07:00
|
|
|
}
|
2009-03-11 11:10:24 -07:00
|
|
|
|
2021-06-26 06:23:51 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MTIME)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_mtime_to_ts(inode, fattr->mtime);
|
2021-06-26 06:23:51 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_MTIME)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_MTIME;
|
2009-08-09 12:06:19 -07:00
|
|
|
|
2021-06-26 06:23:51 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
2023-07-05 12:01:22 -07:00
|
|
|
inode_set_ctime_to_ts(inode, fattr->ctime);
|
2021-06-26 06:23:51 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_CTIME)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_CTIME;
|
2007-09-27 12:57:24 -07:00
|
|
|
|
2005-06-22 10:16:30 -07:00
|
|
|
/* Check if our cached file size is stale */
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
|
|
|
|
new_isize = nfs_size_to_loff_t(fattr->size);
|
|
|
|
cur_isize = i_size_read(inode);
|
2018-06-03 08:38:38 -07:00
|
|
|
if (new_isize != cur_isize && !have_delegation) {
|
2009-03-11 11:10:24 -07:00
|
|
|
/* Do we perhaps have any outstanding writes, or has
|
|
|
|
* the file grown beyond our last write? */
|
2017-08-01 12:39:46 -07:00
|
|
|
if (!nfs_have_writebacks(inode) || new_isize > cur_isize) {
|
2021-10-04 07:10:16 -07:00
|
|
|
trace_nfs_size_update(inode, new_isize);
|
2009-03-11 11:10:24 -07:00
|
|
|
i_size_write(inode, new_isize);
|
2016-06-08 14:08:28 -07:00
|
|
|
if (!have_writers)
|
2018-03-20 13:53:31 -07:00
|
|
|
invalid |= NFS_INO_INVALID_DATA;
|
2009-03-11 11:10:24 -07:00
|
|
|
}
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2021-03-24 09:29:41 -07:00
|
|
|
if (new_isize == 0 &&
|
|
|
|
!(fattr->valid & (NFS_ATTR_FATTR_SPACE_USED |
|
|
|
|
NFS_ATTR_FATTR_BLOCKS_USED))) {
|
|
|
|
fattr->du.nfs3.used = 0;
|
|
|
|
fattr->valid |= NFS_ATTR_FATTR_SPACE_USED;
|
|
|
|
}
|
2021-06-26 06:23:51 -07:00
|
|
|
} else
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_SIZE;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
|
2023-10-04 11:52:36 -07:00
|
|
|
inode_set_atime_to_ts(inode, fattr->atime);
|
2021-06-26 06:23:51 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_ATIME)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_ATIME;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_MODE) {
|
|
|
|
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) {
|
2010-02-03 06:27:35 -07:00
|
|
|
umode_t newmode = inode->i_mode & S_IFMT;
|
|
|
|
newmode |= fattr->mode & S_IALLUGO;
|
|
|
|
inode->i_mode = newmode;
|
2018-03-20 13:53:31 -07:00
|
|
|
invalid |= NFS_INO_INVALID_ACCESS
|
2018-06-03 08:56:05 -07:00
|
|
|
| NFS_INO_INVALID_ACL;
|
2009-03-11 11:10:24 -07:00
|
|
|
}
|
2021-06-26 06:23:51 -07:00
|
|
|
} else if (fattr_supported & NFS_ATTR_FATTR_MODE)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_MODE;
|
2009-08-09 12:06:19 -07:00
|
|
|
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
|
2013-02-01 15:26:23 -07:00
|
|
|
if (!uid_eq(inode->i_uid, fattr->uid)) {
|
2018-03-20 13:53:31 -07:00
|
|
|
invalid |= NFS_INO_INVALID_ACCESS
|
2018-06-03 08:56:05 -07:00
|
|
|
| NFS_INO_INVALID_ACL;
|
2009-03-11 11:10:24 -07:00
|
|
|
inode->i_uid = fattr->uid;
|
|
|
|
}
|
2021-06-26 06:23:51 -07:00
|
|
|
} else if (fattr_supported & NFS_ATTR_FATTR_OWNER)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_OTHER;
|
2009-08-09 12:06:19 -07:00
|
|
|
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
|
2013-02-01 15:26:23 -07:00
|
|
|
if (!gid_eq(inode->i_gid, fattr->gid)) {
|
2018-03-20 13:53:31 -07:00
|
|
|
invalid |= NFS_INO_INVALID_ACCESS
|
2018-06-03 08:56:05 -07:00
|
|
|
| NFS_INO_INVALID_ACL;
|
2009-03-11 11:10:24 -07:00
|
|
|
inode->i_gid = fattr->gid;
|
|
|
|
}
|
2021-06-26 06:23:51 -07:00
|
|
|
} else if (fattr_supported & NFS_ATTR_FATTR_GROUP)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_OTHER;
|
2008-10-14 16:23:07 -07:00
|
|
|
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
|
2021-10-02 16:21:49 -07:00
|
|
|
if (inode->i_nlink != fattr->nlink)
|
2011-10-28 05:13:29 -07:00
|
|
|
set_nlink(inode, fattr->nlink);
|
2021-06-26 06:23:51 -07:00
|
|
|
} else if (fattr_supported & NFS_ATTR_FATTR_NLINK)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_NLINK;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2009-03-11 11:10:24 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* report the blocks in 512byte units
|
|
|
|
*/
|
|
|
|
inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
|
2021-06-26 06:23:51 -07:00
|
|
|
} else if (fattr_supported & NFS_ATTR_FATTR_SPACE_USED)
|
2018-03-05 13:01:18 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_BLOCKS;
|
|
|
|
|
2021-06-26 06:23:51 -07:00
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
|
2009-03-11 11:10:24 -07:00
|
|
|
inode->i_blocks = fattr->du.nfs2.blocks;
|
2021-06-26 06:23:51 -07:00
|
|
|
else if (fattr_supported & NFS_ATTR_FATTR_BLOCKS_USED)
|
2021-04-11 11:31:24 -07:00
|
|
|
nfsi->cache_validity |=
|
|
|
|
save_cache_validity & NFS_INO_INVALID_BLOCKS;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
/* Update attrtimeo value if we're out of the unstable period */
|
2018-06-03 08:56:05 -07:00
|
|
|
if (attr_changed) {
|
2006-03-20 11:44:14 -07:00
|
|
|
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
|
2005-04-16 15:20:36 -07:00
|
|
|
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
|
2007-02-05 15:26:28 -07:00
|
|
|
nfsi->attrtimeo_timestamp = now;
|
2015-02-26 17:34:32 -07:00
|
|
|
/* Set barrier to be more recent than all outstanding updates */
|
2008-10-14 16:16:07 -07:00
|
|
|
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
|
2007-10-01 15:57:50 -07:00
|
|
|
} else {
|
2015-12-29 16:55:19 -07: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-01 15:57:50 -07:00
|
|
|
nfsi->attrtimeo_timestamp = now;
|
|
|
|
}
|
2015-02-26 17:34:32 -07:00
|
|
|
/* Set the barrier to be more recent than this fattr */
|
2021-03-29 13:46:05 -07:00
|
|
|
if ((long)(fattr->gencount - nfsi->attr_gencount) > 0)
|
2015-02-26 17:34:32 -07:00
|
|
|
nfsi->attr_gencount = fattr->gencount;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2015-11-25 11:50:11 -07:00
|
|
|
|
2005-04-16 15:20:36 -07: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;
|
2018-06-03 08:38:38 -07:00
|
|
|
nfs_set_cache_invalid(inode, invalid);
|
2012-12-20 14:52:38 -07:00
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
2005-11-25 15:10:06 -07:00
|
|
|
out_err:
|
2005-04-16 15:20:36 -07: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.)
|
|
|
|
*/
|
2020-04-06 10:39:29 -07:00
|
|
|
nfs_set_inode_stale_locked(inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
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 06:34:33 -07:00
|
|
|
struct inode *nfs_alloc_inode(struct super_block *sb)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi;
|
2022-03-22 14:41:03 -07:00
|
|
|
nfsi = alloc_inode_sb(sb, nfs_inode_cachep, GFP_KERNEL);
|
2005-04-16 15:20:36 -07:00
|
|
|
if (!nfsi)
|
|
|
|
return NULL;
|
2005-08-18 11:24:09 -07:00
|
|
|
nfsi->flags = 0UL;
|
|
|
|
nfsi->cache_validity = 0UL;
|
2023-03-21 15:27:04 -07:00
|
|
|
nfsi->ooo = NULL;
|
2012-07-30 13:05:25 -07:00
|
|
|
#if IS_ENABLED(CONFIG_NFS_V4)
|
2005-06-22 10:16:23 -07:00
|
|
|
nfsi->nfs4_acl = NULL;
|
|
|
|
#endif /* CONFIG_NFS_V4 */
|
2020-06-23 15:39:04 -07:00
|
|
|
#ifdef CONFIG_NFS_V4_2
|
|
|
|
nfsi->xattr_cache = NULL;
|
|
|
|
#endif
|
NFS: Convert buffered read paths to use netfs when fscache is enabled
Convert the NFS buffered read code paths to corresponding netfs APIs,
but only when fscache is configured and enabled.
The netfs API defines struct netfs_request_ops which must be filled
in by the network filesystem. For NFS, we only need to define 5 of
the functions, the main one being the issue_read() function.
The issue_read() function is called by the netfs layer when a read
cannot be fulfilled locally, and must be sent to the server (either
the cache is not active, or it is active but the data is not available).
Once the read from the server is complete, netfs requires a call to
netfs_subreq_terminated() which conveys either how many bytes were read
successfully, or an error. Note that issue_read() is called with a
structure, netfs_io_subrequest, which defines the IO requested, and
contains a start and a length (both in bytes), and assumes the underlying
netfs will return a either an error on the whole region, or the number
of bytes successfully read.
The NFS IO path is page based and the main APIs are the pgio APIs defined
in pagelist.c. For the pgio APIs, there is no way for the caller to
know how many RPCs will be sent and how the pages will be broken up
into underlying RPCs, each of which will have their own completion and
return code. In contrast, netfs is subrequest based, a single
subrequest may contain multiple pages, and a single subrequest is
initiated with issue_read() and terminated with netfs_subreq_terminated().
Thus, to utilze the netfs APIs, NFS needs some way to accommodate
the netfs API requirement on the single response to the whole
subrequest, while also minimizing disruptive changes to the NFS
pgio layer.
The approach taken with this patch is to allocate a small structure
for each nfs_netfs_issue_read() call, store the final error and number
of bytes successfully transferred in the structure, and update these values
as each RPC completes. The refcount on the structure is used as a marker
for the last RPC completion, is incremented in nfs_netfs_read_initiate(),
and decremented inside nfs_netfs_read_completion(), when a nfs_pgio_header
contains a valid pointer to the data. On the final put (which signals
the final outstanding RPC is complete) in nfs_netfs_read_completion(),
call netfs_subreq_terminated() with either the final error value (if
one or more READs complete with an error) or the number of bytes
successfully transferred (if all RPCs complete successfully). Note
that when all RPCs complete successfully, the number of bytes transferred
is capped to the length of the subrequest. Capping the transferred length
to the subrequest length prevents "Subreq overread" warnings from netfs.
This is due to the "aligned_len" in nfs_pageio_add_page(), and the
corner case where NFS requests a full page at the end of the file,
even when i_size reflects only a partial page (NFS overread).
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
Tested-by: Daire Byrne <daire@dneg.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
2023-02-20 06:43:06 -07:00
|
|
|
nfs_netfs_inode_init(nfsi);
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
return &nfsi->vfs_inode;
|
|
|
|
}
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_alloc_inode);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2019-04-15 17:19:40 -07:00
|
|
|
void nfs_free_inode(struct inode *inode)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2023-03-21 15:27:04 -07:00
|
|
|
kfree(NFS_I(inode)->ooo);
|
2005-04-16 15:20:36 -07:00
|
|
|
kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
|
|
|
|
}
|
2019-04-15 17:19:40 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_free_inode);
|
2011-01-06 23:49:49 -07:00
|
|
|
|
2006-06-25 02:41:26 -07:00
|
|
|
static inline void nfs4_init_once(struct nfs_inode *nfsi)
|
|
|
|
{
|
2012-07-30 13:05:25 -07:00
|
|
|
#if IS_ENABLED(CONFIG_NFS_V4)
|
2006-06-25 02:41:26 -07:00
|
|
|
INIT_LIST_HEAD(&nfsi->open_states);
|
|
|
|
nfsi->delegation = NULL;
|
|
|
|
init_rwsem(&nfsi->rwsem);
|
2010-10-19 21:18:01 -07:00
|
|
|
nfsi->layout = NULL;
|
2006-06-25 02:41:26 -07: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 06:34:33 -07:00
|
|
|
|
2008-07-25 19:45:34 -07:00
|
|
|
static void init_once(void *foo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2022-06-14 22:39:24 -07:00
|
|
|
struct nfs_inode *nfsi = foo;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2007-05-16 22:10:57 -07: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);
|
|
|
|
nfs4_init_once(nfsi);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2007-07-19 18:11:58 -07: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 06:34:33 -07:00
|
|
|
static int __init nfs_init_inodecache(void)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
|
|
|
|
sizeof(struct nfs_inode),
|
2006-03-24 04:16:06 -07:00
|
|
|
0, (SLAB_RECLAIM_ACCOUNT|
|
2016-01-14 16:18:21 -07:00
|
|
|
SLAB_MEM_SPREAD|SLAB_ACCOUNT),
|
2007-07-19 18:11:58 -07:00
|
|
|
init_once);
|
2005-04-16 15:20:36 -07:00
|
|
|
if (nfs_inode_cachep == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-27 12:59:15 -07:00
|
|
|
static void nfs_destroy_inodecache(void)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2012-09-25 18:33:07 -07:00
|
|
|
/*
|
|
|
|
* Make sure all delayed rcu free inodes are flushed before we
|
|
|
|
* destroy cache.
|
|
|
|
*/
|
|
|
|
rcu_barrier();
|
2006-09-27 01:49:40 -07:00
|
|
|
kmem_cache_destroy(nfs_inode_cachep);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
2008-02-19 18:04:22 -07:00
|
|
|
struct workqueue_struct *nfsiod_workqueue;
|
2012-07-30 13:05:25 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfsiod_workqueue);
|
2008-02-19 18:04:22 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* start up the nfsiod workqueue
|
|
|
|
*/
|
|
|
|
static int nfsiod_start(void)
|
|
|
|
{
|
|
|
|
struct workqueue_struct *wq;
|
|
|
|
dprintk("RPC: creating workqueue nfsiod\n");
|
2020-11-26 17:24:33 -07:00
|
|
|
wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
|
2008-02-19 18:04:22 -07: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-16 18:58:21 -07:00
|
|
|
unsigned int nfs_net_id;
|
2012-01-10 06:04:24 -07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs_net_id);
|
2011-11-25 07:13:04 -07:00
|
|
|
|
|
|
|
static int nfs_net_init(struct net *net)
|
|
|
|
{
|
2012-01-23 10:26:05 -07:00
|
|
|
nfs_clients_init(net);
|
2014-07-31 04:35:20 -07:00
|
|
|
return nfs_fs_proc_net_init(net);
|
2011-11-25 07:13:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs_net_exit(struct net *net)
|
|
|
|
{
|
2014-07-31 04:35:20 -07:00
|
|
|
nfs_fs_proc_net_exit(net);
|
2019-02-06 07:32:50 -07:00
|
|
|
nfs_clients_exit(net);
|
2011-11-25 07:13:04 -07: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-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* Initialize NFS
|
|
|
|
*/
|
|
|
|
static int __init init_nfs_fs(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2019-01-24 14:10:46 -07:00
|
|
|
err = nfs_sysfs_init();
|
|
|
|
if (err < 0)
|
|
|
|
goto out10;
|
|
|
|
|
2011-11-25 07:13:04 -07:00
|
|
|
err = register_pernet_subsys(&nfs_net_ops);
|
2009-08-19 15:12:27 -07:00
|
|
|
if (err < 0)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out9;
|
2009-08-19 15:12:27 -07:00
|
|
|
|
2008-02-19 18:04:22 -07:00
|
|
|
err = nfsiod_start();
|
|
|
|
if (err)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out7;
|
2008-02-19 18:04:22 -07:00
|
|
|
|
2006-08-22 17:06:13 -07:00
|
|
|
err = nfs_fs_proc_init();
|
|
|
|
if (err)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out6;
|
2006-08-22 17:06:13 -07:00
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
err = nfs_init_nfspagecache();
|
|
|
|
if (err)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out5;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
err = nfs_init_inodecache();
|
|
|
|
if (err)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out4;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
err = nfs_init_readpagecache();
|
|
|
|
if (err)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out3;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
err = nfs_init_writepagecache();
|
|
|
|
if (err)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out2;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
err = nfs_init_directcache();
|
|
|
|
if (err)
|
2012-07-30 13:05:25 -07:00
|
|
|
goto out1;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2011-12-06 06:42:40 -07:00
|
|
|
rpc_proc_register(&init_net, &nfs_rpcstat);
|
2015-06-30 20:58:31 -07:00
|
|
|
|
|
|
|
err = register_nfs_fs();
|
|
|
|
if (err)
|
2012-07-16 13:39:13 -07:00
|
|
|
goto out0;
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
2012-07-16 13:39:13 -07:00
|
|
|
out0:
|
2011-12-06 06:42:40 -07:00
|
|
|
rpc_proc_unregister(&init_net, "nfs");
|
2005-04-16 15:20:36 -07:00
|
|
|
nfs_destroy_directcache();
|
2012-07-30 13:05:25 -07:00
|
|
|
out1:
|
2012-07-16 13:39:13 -07:00
|
|
|
nfs_destroy_writepagecache();
|
2012-07-30 13:05:25 -07:00
|
|
|
out2:
|
2012-07-16 13:39:13 -07:00
|
|
|
nfs_destroy_readpagecache();
|
2012-07-30 13:05:25 -07:00
|
|
|
out3:
|
2012-07-16 13:39:13 -07:00
|
|
|
nfs_destroy_inodecache();
|
2012-07-30 13:05:25 -07:00
|
|
|
out4:
|
2012-07-16 13:39:13 -07:00
|
|
|
nfs_destroy_nfspagecache();
|
2012-07-30 13:05:25 -07:00
|
|
|
out5:
|
2012-07-16 13:39:13 -07:00
|
|
|
nfs_fs_proc_exit();
|
2012-07-30 13:05:25 -07:00
|
|
|
out6:
|
2012-07-16 13:39:13 -07:00
|
|
|
nfsiod_stop();
|
2012-07-30 13:05:25 -07:00
|
|
|
out7:
|
2012-07-16 13:39:13 -07:00
|
|
|
unregister_pernet_subsys(&nfs_net_ops);
|
2012-07-30 13:05:25 -07:00
|
|
|
out9:
|
2019-01-24 14:10:46 -07:00
|
|
|
nfs_sysfs_exit();
|
|
|
|
out10:
|
2005-04-16 15:20:36 -07: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();
|
2011-11-25 07:13:04 -07:00
|
|
|
unregister_pernet_subsys(&nfs_net_ops);
|
2011-12-06 06:42:40 -07: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 06:34:33 -07:00
|
|
|
unregister_nfs_fs();
|
2006-08-22 17:06:13 -07:00
|
|
|
nfs_fs_proc_exit();
|
2008-02-19 18:04:22 -07:00
|
|
|
nfsiod_stop();
|
2019-01-24 14:10:46 -07:00
|
|
|
nfs_sysfs_exit();
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Not quite true; I just maintain it */
|
|
|
|
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
|
|
|
|
MODULE_LICENSE("GPL");
|
2007-10-09 09:01:04 -07:00
|
|
|
module_param(enable_ino64, bool, 0644);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
module_init(init_nfs_fs)
|
|
|
|
module_exit(exit_nfs_fs)
|