NFSv4: Add support for delegated atime and mtime attributes

Ensure that we update the mtime and atime correctly when we read
or write data to the file and when we truncate. Let the server manage
ctime on other attribute updates.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
Trond Myklebust 2024-06-16 21:21:25 -04:00 committed by Anna Schumaker
parent 4201916f2a
commit e12912d941
6 changed files with 124 additions and 20 deletions

View File

@ -115,6 +115,9 @@ static int nfs4_do_check_delegation(struct inode *inode, fmode_t type,
if (mark)
nfs_mark_delegation_referenced(delegation);
ret = 1;
if ((flags & NFS_DELEGATION_FLAG_TIME) &&
!test_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags))
ret = 0;
}
rcu_read_unlock();
return ret;
@ -221,11 +224,12 @@ again:
* @type: delegation type
* @stateid: delegation stateid
* @pagemod_limit: write delegation "space_limit"
* @deleg_type: raw delegation type
*
*/
void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
fmode_t type, const nfs4_stateid *stateid,
unsigned long pagemod_limit)
unsigned long pagemod_limit, u32 deleg_type)
{
struct nfs_delegation *delegation;
const struct cred *oldcred = NULL;
@ -239,6 +243,14 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
delegation->pagemod_limit = pagemod_limit;
oldcred = delegation->cred;
delegation->cred = get_cred(cred);
switch (deleg_type) {
case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
set_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
break;
default:
clear_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
}
clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
if (test_and_clear_bit(NFS_DELEGATION_REVOKED,
&delegation->flags))
@ -250,7 +262,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
} else {
rcu_read_unlock();
nfs_inode_set_delegation(inode, cred, type, stateid,
pagemod_limit);
pagemod_limit, deleg_type);
}
}
@ -418,13 +430,13 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
* @type: delegation type
* @stateid: delegation stateid
* @pagemod_limit: write delegation "space_limit"
* @deleg_type: raw delegation type
*
* Returns zero on success, or a negative errno value.
*/
int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
fmode_t type,
const nfs4_stateid *stateid,
unsigned long pagemod_limit)
fmode_t type, const nfs4_stateid *stateid,
unsigned long pagemod_limit, u32 deleg_type)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_client *clp = server->nfs_client;
@ -444,6 +456,11 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
delegation->cred = get_cred(cred);
delegation->inode = inode;
delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
switch (deleg_type) {
case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
delegation->flags |= BIT(NFS_DELEGATION_DELEGTIME);
}
delegation->test_gen = 0;
spin_lock_init(&delegation->lock);
@ -508,6 +525,11 @@ add_new:
atomic_long_inc(&nfs_active_delegations);
trace_nfs4_set_delegation(inode, type);
/* If we hold writebacks and have delegated mtime then update */
if (deleg_type == NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG &&
nfs_have_writebacks(inode))
nfs_update_delegated_mtime(inode);
out:
spin_unlock(&clp->cl_lock);
if (delegation != NULL)

View File

@ -38,12 +38,15 @@ enum {
NFS_DELEGATION_TEST_EXPIRED,
NFS_DELEGATION_INODE_FREEING,
NFS_DELEGATION_RETURN_DELAYED,
NFS_DELEGATION_DELEGTIME,
};
int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit);
fmode_t type, const nfs4_stateid *stateid,
unsigned long pagemod_limit, u32 deleg_type);
void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit);
fmode_t type, const nfs4_stateid *stateid,
unsigned long pagemod_limit, u32 deleg_type);
int nfs4_inode_return_delegation(struct inode *inode);
void nfs4_inode_return_delegation_on_close(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
@ -84,6 +87,12 @@ int nfs4_inode_make_writeable(struct inode *inode);
#endif
#define NFS_DELEGATION_FLAG_TIME BIT(1)
void nfs_update_delegated_atime(struct inode *inode);
void nfs_update_delegated_mtime(struct inode *inode);
void nfs_update_delegated_mtime_locked(struct inode *inode);
static inline int nfs_have_read_or_write_delegation(struct inode *inode)
{
return NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0);
@ -99,4 +108,16 @@ static inline int nfs_have_delegated_attributes(struct inode *inode)
return NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0);
}
static inline int nfs_have_delegated_atime(struct inode *inode)
{
return NFS_PROTO(inode)->have_delegation(inode, FMODE_READ,
NFS_DELEGATION_FLAG_TIME);
}
static inline int nfs_have_delegated_mtime(struct inode *inode)
{
return NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE,
NFS_DELEGATION_FLAG_TIME);
}
#endif

View File

@ -275,6 +275,8 @@ EXPORT_SYMBOL_GPL(nfs_zap_acl_cache);
void nfs_invalidate_atime(struct inode *inode)
{
if (nfs_have_delegated_atime(inode))
return;
spin_lock(&inode->i_lock);
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
spin_unlock(&inode->i_lock);
@ -604,6 +606,33 @@ out_no_inode:
}
EXPORT_SYMBOL_GPL(nfs_fhget);
void nfs_update_delegated_atime(struct inode *inode)
{
spin_lock(&inode->i_lock);
if (nfs_have_delegated_atime(inode)) {
inode_update_timestamps(inode, S_ATIME);
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ATIME;
}
spin_unlock(&inode->i_lock);
}
void nfs_update_delegated_mtime_locked(struct inode *inode)
{
if (nfs_have_delegated_mtime(inode)) {
inode_update_timestamps(inode, S_CTIME | S_MTIME);
NFS_I(inode)->cache_validity &= ~(NFS_INO_INVALID_CTIME |
NFS_INO_INVALID_MTIME);
}
}
void nfs_update_delegated_mtime(struct inode *inode)
{
spin_lock(&inode->i_lock);
nfs_update_delegated_mtime_locked(inode);
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_update_delegated_mtime);
#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)
int
@ -631,6 +660,17 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
attr->ia_valid &= ~ATTR_SIZE;
}
if (nfs_have_delegated_mtime(inode)) {
if (attr->ia_valid & ATTR_MTIME) {
nfs_update_delegated_mtime(inode);
attr->ia_valid &= ~ATTR_MTIME;
}
if (attr->ia_valid & ATTR_ATIME) {
nfs_update_delegated_atime(inode);
attr->ia_valid &= ~ATTR_ATIME;
}
}
/* Optimization: if the end result is no change, don't RPC */
if (((attr->ia_valid & NFS_VALID_ATTRS) & ~(ATTR_FILE|ATTR_OPEN)) == 0)
return 0;
@ -686,6 +726,7 @@ static int nfs_vmtruncate(struct inode * inode, loff_t offset)
spin_unlock(&inode->i_lock);
truncate_pagecache(inode, offset);
nfs_update_delegated_mtime_locked(inode);
spin_lock(&inode->i_lock);
out:
return err;
@ -709,8 +750,9 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
spin_lock(&inode->i_lock);
NFS_I(inode)->attr_gencount = fattr->gencount;
if ((attr->ia_valid & ATTR_SIZE) != 0) {
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME |
NFS_INO_INVALID_BLOCKS);
if (!nfs_have_delegated_mtime(inode))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS);
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
nfs_vmtruncate(inode, attr->ia_size);
}
@ -856,8 +898,12 @@ int nfs_getattr(struct mnt_idmap *idmap, const struct path *path,
/* Flush out writes to the server in order to update c/mtime/version. */
if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_CHANGE_COOKIE)) &&
S_ISREG(inode->i_mode))
S_ISREG(inode->i_mode)) {
if (nfs_have_delegated_mtime(inode))
filemap_fdatawrite(inode->i_mapping);
else
filemap_write_and_wait(inode->i_mapping);
}
/*
* We may force a getattr if the user cares about atime.

View File

@ -1245,6 +1245,7 @@ nfs4_update_changeattr_locked(struct inode *inode,
struct nfs_inode *nfsi = NFS_I(inode);
u64 change_attr = inode_peek_iversion_raw(inode);
if (!nfs_have_delegated_mtime(inode))
cache_validity |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
if (S_ISDIR(inode->i_mode))
cache_validity |= NFS_INO_INVALID_DATA;
@ -1961,6 +1962,8 @@ nfs4_process_delegation(struct inode *inode, const struct cred *cred,
switch (delegation->open_delegation_type) {
case NFS4_OPEN_DELEGATE_READ:
case NFS4_OPEN_DELEGATE_WRITE:
case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
break;
default:
return;
@ -1974,16 +1977,16 @@ nfs4_process_delegation(struct inode *inode, const struct cred *cred,
NFS_SERVER(inode)->nfs_client->cl_hostname);
break;
case NFS4_OPEN_CLAIM_PREVIOUS:
nfs_inode_reclaim_delegation(inode, cred,
delegation->type,
nfs_inode_reclaim_delegation(inode, cred, delegation->type,
&delegation->stateid,
delegation->pagemod_limit);
delegation->pagemod_limit,
delegation->open_delegation_type);
break;
default:
nfs_inode_set_delegation(inode, cred,
delegation->type,
nfs_inode_set_delegation(inode, cred, delegation->type,
&delegation->stateid,
delegation->pagemod_limit);
delegation->pagemod_limit,
delegation->open_delegation_type);
}
if (delegation->do_recall)
nfs_async_inode_return_delegation(inode, &delegation->stateid);

View File

@ -28,6 +28,7 @@
#include "fscache.h"
#include "pnfs.h"
#include "nfstrace.h"
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
@ -372,6 +373,7 @@ int nfs_read_folio(struct file *file, struct folio *folio)
goto out_put;
nfs_pageio_complete_read(&pgio);
nfs_update_delegated_atime(inode);
ret = pgio.pg_error < 0 ? pgio.pg_error : 0;
if (!ret) {
ret = folio_wait_locked_killable(folio);
@ -428,6 +430,7 @@ void nfs_readahead(struct readahead_control *ractl)
}
nfs_pageio_complete_read(&pgio);
nfs_update_delegated_atime(inode);
put_nfs_open_context(ctx);
out:

View File

@ -289,6 +289,8 @@ static void nfs_grow_file(struct folio *folio, unsigned int offset,
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
out:
/* Atomically update timestamps if they are delegated to us. */
nfs_update_delegated_mtime_locked(inode);
spin_unlock(&inode->i_lock);
nfs_fscache_invalidate(inode, 0);
}
@ -1514,6 +1516,13 @@ void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
struct nfs_fattr *fattr = &hdr->fattr;
struct inode *inode = hdr->inode;
if (nfs_have_delegated_mtime(inode)) {
spin_lock(&inode->i_lock);
nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS);
spin_unlock(&inode->i_lock);
return;
}
spin_lock(&inode->i_lock);
nfs_writeback_check_extend(hdr, fattr);
nfs_post_op_update_inode_force_wcc_locked(inode, fattr);