Merge tag 'nfs-for-6.12-3' of git://git.linux-nfs.org/projects/anna/linux-nfs

Pull NFS client fixes from Anna Schumaker:
 "These are mostly fixes that came up during the nfs bakeathon the other
  week.

  Stable Fixes:
   - Fix KMSAN warning in decode_getfattr_attrs()

  Other Bugfixes:
   - Handle -ENOTCONN in xs_tcp_setup_socked()
   - NFSv3: only use NFS timeout for MOUNT when protocols are compatible
   - Fix attribute delegation behavior on exclusive create and a/mtime
     changes
   - Fix localio to cope with racing nfs_local_probe()
   - Avoid i_lock contention in fs_clear_invalid_mapping()"

* tag 'nfs-for-6.12-3' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  nfs: avoid i_lock contention in nfs_clear_invalid_mapping
  nfs_common: fix localio to cope with racing nfs_local_probe()
  NFS: Further fixes to attribute delegation a/mtime changes
  NFS: Fix attribute delegation behaviour on exclusive create
  nfs: Fix KMSAN warning in decode_getfattr_attrs()
  NFSv3: only use NFS timeout for MOUNT when protocols are compatible
  sunrpc: handle -ENOTCONN in xs_tcp_setup_socket()
This commit is contained in:
Linus Torvalds
2024-11-06 13:09:22 -10:00
8 changed files with 84 additions and 33 deletions
+1 -2
View File
@@ -181,8 +181,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
#if IS_ENABLED(CONFIG_NFS_LOCALIO) #if IS_ENABLED(CONFIG_NFS_LOCALIO)
seqlock_init(&clp->cl_boot_lock); seqlock_init(&clp->cl_boot_lock);
ktime_get_real_ts64(&clp->cl_nfssvc_boot); ktime_get_real_ts64(&clp->cl_nfssvc_boot);
clp->cl_uuid.net = NULL; nfs_uuid_init(&clp->cl_uuid);
clp->cl_uuid.dom = NULL;
spin_lock_init(&clp->cl_localio_lock); spin_lock_init(&clp->cl_localio_lock);
#endif /* CONFIG_NFS_LOCALIO */ #endif /* CONFIG_NFS_LOCALIO */
+47 -23
View File
@@ -205,12 +205,15 @@ void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
nfs_fscache_invalidate(inode, 0); nfs_fscache_invalidate(inode, 0);
flags &= ~NFS_INO_REVAL_FORCED; flags &= ~NFS_INO_REVAL_FORCED;
nfsi->cache_validity |= flags; flags |= nfsi->cache_validity;
if (inode->i_mapping->nrpages == 0)
flags &= ~NFS_INO_INVALID_DATA;
if (inode->i_mapping->nrpages == 0) { /* pairs with nfs_clear_invalid_mapping()'s smp_load_acquire() */
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; smp_store_release(&nfsi->cache_validity, flags);
nfs_ooo_clear(nfsi);
} else if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { if (inode->i_mapping->nrpages == 0 ||
nfsi->cache_validity & NFS_INO_INVALID_DATA) {
nfs_ooo_clear(nfsi); nfs_ooo_clear(nfsi);
} }
trace_nfs_set_cache_invalid(inode, 0); trace_nfs_set_cache_invalid(inode, 0);
@@ -628,23 +631,35 @@ nfs_fattr_fixup_delegated(struct inode *inode, struct nfs_fattr *fattr)
} }
} }
static void nfs_update_timestamps(struct inode *inode, unsigned int ia_valid)
{
enum file_time_flags time_flags = 0;
unsigned int cache_flags = 0;
if (ia_valid & ATTR_MTIME) {
time_flags |= S_MTIME | S_CTIME;
cache_flags |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
}
if (ia_valid & ATTR_ATIME) {
time_flags |= S_ATIME;
cache_flags |= NFS_INO_INVALID_ATIME;
}
inode_update_timestamps(inode, time_flags);
NFS_I(inode)->cache_validity &= ~cache_flags;
}
void nfs_update_delegated_atime(struct inode *inode) void nfs_update_delegated_atime(struct inode *inode)
{ {
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (nfs_have_delegated_atime(inode)) { if (nfs_have_delegated_atime(inode))
inode_update_timestamps(inode, S_ATIME); nfs_update_timestamps(inode, ATTR_ATIME);
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ATIME;
}
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
} }
void nfs_update_delegated_mtime_locked(struct inode *inode) void nfs_update_delegated_mtime_locked(struct inode *inode)
{ {
if (nfs_have_delegated_mtime(inode)) { if (nfs_have_delegated_mtime(inode))
inode_update_timestamps(inode, S_CTIME | S_MTIME); nfs_update_timestamps(inode, ATTR_MTIME);
NFS_I(inode)->cache_validity &= ~(NFS_INO_INVALID_CTIME |
NFS_INO_INVALID_MTIME);
}
} }
void nfs_update_delegated_mtime(struct inode *inode) void nfs_update_delegated_mtime(struct inode *inode)
@@ -682,15 +697,16 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
attr->ia_valid &= ~ATTR_SIZE; attr->ia_valid &= ~ATTR_SIZE;
} }
if (nfs_have_delegated_mtime(inode)) { if (nfs_have_delegated_mtime(inode) && attr->ia_valid & ATTR_MTIME) {
if (attr->ia_valid & ATTR_MTIME) { spin_lock(&inode->i_lock);
nfs_update_delegated_mtime(inode); nfs_update_timestamps(inode, attr->ia_valid);
attr->ia_valid &= ~ATTR_MTIME; spin_unlock(&inode->i_lock);
} attr->ia_valid &= ~(ATTR_MTIME | ATTR_ATIME);
if (attr->ia_valid & ATTR_ATIME) { } else if (nfs_have_delegated_atime(inode) &&
nfs_update_delegated_atime(inode); attr->ia_valid & ATTR_ATIME &&
attr->ia_valid &= ~ATTR_ATIME; !(attr->ia_valid & ATTR_MTIME)) {
} nfs_update_delegated_atime(inode);
attr->ia_valid &= ~ATTR_ATIME;
} }
/* Optimization: if the end result is no change, don't RPC */ /* Optimization: if the end result is no change, don't RPC */
@@ -1408,6 +1424,13 @@ int nfs_clear_invalid_mapping(struct address_space *mapping)
TASK_KILLABLE|TASK_FREEZABLE_UNSAFE); TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
if (ret) if (ret)
goto out; goto out;
smp_rmb(); /* pairs with smp_wmb() below */
if (test_bit(NFS_INO_INVALIDATING, bitlock))
continue;
/* pairs with nfs_set_cache_invalid()'s smp_store_release() */
if (!(smp_load_acquire(&nfsi->cache_validity) & NFS_INO_INVALID_DATA))
goto out;
/* Slow-path that double-checks with spinlock held */
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (test_bit(NFS_INO_INVALIDATING, bitlock)) { if (test_bit(NFS_INO_INVALIDATING, bitlock)) {
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
@@ -1633,6 +1656,7 @@ void nfs_fattr_init(struct nfs_fattr *fattr)
fattr->gencount = nfs_inc_attr_generation_counter(); fattr->gencount = nfs_inc_attr_generation_counter();
fattr->owner_name = NULL; fattr->owner_name = NULL;
fattr->group_name = NULL; fattr->group_name = NULL;
fattr->mdsthreshold = NULL;
} }
EXPORT_SYMBOL_GPL(nfs_fattr_init); EXPORT_SYMBOL_GPL(nfs_fattr_init);
+2 -1
View File
@@ -205,7 +205,8 @@ void nfs_local_probe(struct nfs_client *clp)
nfs_local_disable(clp); nfs_local_disable(clp);
} }
nfs_uuid_begin(&clp->cl_uuid); if (!nfs_uuid_begin(&clp->cl_uuid))
return;
if (nfs_server_uuid_is_local(clp)) if (nfs_server_uuid_is_local(clp))
nfs_local_enable(clp); nfs_local_enable(clp);
nfs_uuid_end(&clp->cl_uuid); nfs_uuid_end(&clp->cl_uuid);
+4
View File
@@ -3452,6 +3452,10 @@ static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
adjust_flags |= NFS_INO_INVALID_MODE; adjust_flags |= NFS_INO_INVALID_MODE;
if (sattr->ia_valid & (ATTR_UID | ATTR_GID)) if (sattr->ia_valid & (ATTR_UID | ATTR_GID))
adjust_flags |= NFS_INO_INVALID_OTHER; adjust_flags |= NFS_INO_INVALID_OTHER;
if (sattr->ia_valid & ATTR_ATIME)
adjust_flags |= NFS_INO_INVALID_ATIME;
if (sattr->ia_valid & ATTR_MTIME)
adjust_flags |= NFS_INO_INVALID_MTIME;
do { do {
nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, fattr->label), nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, fattr->label),
+9 -1
View File
@@ -885,7 +885,15 @@ static int nfs_request_mount(struct fs_context *fc,
* Now ask the mount server to map our export path * Now ask the mount server to map our export path
* to a file handle. * to a file handle.
*/ */
status = nfs_mount(&request, ctx->timeo, ctx->retrans); if ((request.protocol == XPRT_TRANSPORT_UDP) ==
!(ctx->flags & NFS_MOUNT_TCP))
/*
* NFS protocol and mount protocol are both UDP or neither UDP
* so timeouts are compatible. Use NFS timeouts for MOUNT
*/
status = nfs_mount(&request, ctx->timeo, ctx->retrans);
else
status = nfs_mount(&request, NFS_UNSPEC_TIMEO, NFS_UNSPEC_RETRANS);
if (status != 0) { if (status != 0) {
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n", dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
request.hostname, status); request.hostname, status);
+18 -5
View File
@@ -5,7 +5,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/rculist.h> #include <linux/list.h>
#include <linux/nfslocalio.h> #include <linux/nfslocalio.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
@@ -20,15 +20,27 @@ static DEFINE_SPINLOCK(nfs_uuid_lock);
*/ */
static LIST_HEAD(nfs_uuids); static LIST_HEAD(nfs_uuids);
void nfs_uuid_begin(nfs_uuid_t *nfs_uuid) void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
{ {
nfs_uuid->net = NULL; nfs_uuid->net = NULL;
nfs_uuid->dom = NULL; nfs_uuid->dom = NULL;
uuid_gen(&nfs_uuid->uuid); INIT_LIST_HEAD(&nfs_uuid->list);
}
EXPORT_SYMBOL_GPL(nfs_uuid_init);
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
{
spin_lock(&nfs_uuid_lock); spin_lock(&nfs_uuid_lock);
list_add_tail_rcu(&nfs_uuid->list, &nfs_uuids); /* Is this nfs_uuid already in use? */
if (!list_empty(&nfs_uuid->list)) {
spin_unlock(&nfs_uuid_lock);
return false;
}
uuid_gen(&nfs_uuid->uuid);
list_add_tail(&nfs_uuid->list, &nfs_uuids);
spin_unlock(&nfs_uuid_lock); spin_unlock(&nfs_uuid_lock);
return true;
} }
EXPORT_SYMBOL_GPL(nfs_uuid_begin); EXPORT_SYMBOL_GPL(nfs_uuid_begin);
@@ -36,7 +48,8 @@ void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
{ {
if (nfs_uuid->net == NULL) { if (nfs_uuid->net == NULL) {
spin_lock(&nfs_uuid_lock); spin_lock(&nfs_uuid_lock);
list_del_init(&nfs_uuid->list); if (nfs_uuid->net == NULL)
list_del_init(&nfs_uuid->list);
spin_unlock(&nfs_uuid_lock); spin_unlock(&nfs_uuid_lock);
} }
} }
+2 -1
View File
@@ -32,7 +32,8 @@ typedef struct {
struct auth_domain *dom; /* auth_domain for localio */ struct auth_domain *dom; /* auth_domain for localio */
} nfs_uuid_t; } nfs_uuid_t;
void nfs_uuid_begin(nfs_uuid_t *); void nfs_uuid_init(nfs_uuid_t *);
bool nfs_uuid_begin(nfs_uuid_t *);
void nfs_uuid_end(nfs_uuid_t *); void nfs_uuid_end(nfs_uuid_t *);
void nfs_uuid_is_local(const uuid_t *, struct list_head *, void nfs_uuid_is_local(const uuid_t *, struct list_head *,
struct net *, struct auth_domain *, struct module *); struct net *, struct auth_domain *, struct module *);
+1
View File
@@ -2459,6 +2459,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
case -EHOSTUNREACH: case -EHOSTUNREACH:
case -EADDRINUSE: case -EADDRINUSE:
case -ENOBUFS: case -ENOBUFS:
case -ENOTCONN:
break; break;
default: default:
printk("%s: connect returned unhandled error %d\n", printk("%s: connect returned unhandled error %d\n",