More NFS Client Bugfixes for Linux 6.12-rc

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()
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmcr1HUACgkQ18tUv7Cl
 QOtdYBAA0YohWDHflcHPbltJu0UyCyDDtowvpVacSDJwZwVEXnLQRTTqrdUnWVxx
 Bc2Ae8tGsfcwo10yZ6LUIPjcyEqLQeYvKoKv2Awf0j7eubjRYZrQVypIKtmy8aC2
 H5ETCyrbIubE06jX8EPO8LFxQ+T6nGD7kC8qJZL8z/aNVXGA2nRRCi7AzdE4o6Ht
 0t6fC+W5vxJ4hQHYKb59nGvREMwpKSLg2U4wo1lyFvkDxEJ06DobGOKEtD333cI8
 Mou/1UlSZ6RzgfwJNIPMMpCepIp2spaDeet0XVN+zqzxg55Jmk7LqpxP5pswTjLb
 WsxErV9ZRXtwutCCf+IDoMCv/YS4g4ZG7CLKXQ4felKJVYIuiS4z0n659xRqLyyi
 nW71vrRUdOBE3rCXUW6crZYwX/fHDvl6bsq9/h7cy2ZPnbGkVvXx+LIm0dJRenfb
 MaxVM3CyrMnzL3UUk/caK/rVCOHrDD5q/dAtSNfizMWnqoX+gXby3ho6Zwn0Wj89
 NiUZJIRI/s4V1WzMw4g+Daz7LUUwGblODTtphH2nnKRDfTiYXeT/r/waU6zUOVcS
 7Jd285DF/tkQp2SJ3nvsM/ni7TD2UuG2BsKA3Urlht9i32lwyENeS3nNcx6aHo3i
 blNpD+9mp3vZfWWZNVvLM/JldcIqEvd30+P6GWwS/Td8Zz4PYIM=
 =9mwu
 -----END PGP SIGNATURE-----

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
commit ff7afaeca1
8 changed files with 84 additions and 33 deletions

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)
seqlock_init(&clp->cl_boot_lock);
ktime_get_real_ts64(&clp->cl_nfssvc_boot);
clp->cl_uuid.net = NULL;
clp->cl_uuid.dom = NULL;
nfs_uuid_init(&clp->cl_uuid);
spin_lock_init(&clp->cl_localio_lock);
#endif /* CONFIG_NFS_LOCALIO */

View File

@ -205,12 +205,15 @@ void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
nfs_fscache_invalidate(inode, 0);
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) {
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
nfs_ooo_clear(nfsi);
} else if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
/* pairs with nfs_clear_invalid_mapping()'s smp_load_acquire() */
smp_store_release(&nfsi->cache_validity, flags);
if (inode->i_mapping->nrpages == 0 ||
nfsi->cache_validity & NFS_INO_INVALID_DATA) {
nfs_ooo_clear(nfsi);
}
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)
{
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;
}
if (nfs_have_delegated_atime(inode))
nfs_update_timestamps(inode, ATTR_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);
}
if (nfs_have_delegated_mtime(inode))
nfs_update_timestamps(inode, ATTR_MTIME);
}
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;
}
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;
}
if (nfs_have_delegated_mtime(inode) && attr->ia_valid & ATTR_MTIME) {
spin_lock(&inode->i_lock);
nfs_update_timestamps(inode, attr->ia_valid);
spin_unlock(&inode->i_lock);
attr->ia_valid &= ~(ATTR_MTIME | ATTR_ATIME);
} else if (nfs_have_delegated_atime(inode) &&
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 */
@ -1408,6 +1424,13 @@ int nfs_clear_invalid_mapping(struct address_space *mapping)
TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
if (ret)
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);
if (test_bit(NFS_INO_INVALIDATING, bitlock)) {
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->owner_name = NULL;
fattr->group_name = NULL;
fattr->mdsthreshold = NULL;
}
EXPORT_SYMBOL_GPL(nfs_fattr_init);

View File

@ -205,7 +205,8 @@ void nfs_local_probe(struct nfs_client *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))
nfs_local_enable(clp);
nfs_uuid_end(&clp->cl_uuid);

View File

@ -3452,6 +3452,10 @@ static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
adjust_flags |= NFS_INO_INVALID_MODE;
if (sattr->ia_valid & (ATTR_UID | ATTR_GID))
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 {
nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, fattr->label),

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
* 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) {
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
request.hostname, status);

View File

@ -5,7 +5,7 @@
*/
#include <linux/module.h>
#include <linux/rculist.h>
#include <linux/list.h>
#include <linux/nfslocalio.h>
#include <net/netns/generic.h>
@ -20,15 +20,27 @@ static DEFINE_SPINLOCK(nfs_uuid_lock);
*/
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->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);
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);
return true;
}
EXPORT_SYMBOL_GPL(nfs_uuid_begin);
@ -36,7 +48,8 @@ void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
{
if (nfs_uuid->net == NULL) {
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);
}
}

View File

@ -32,7 +32,8 @@ typedef struct {
struct auth_domain *dom; /* auth_domain for localio */
} 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_is_local(const uuid_t *, struct list_head *,
struct net *, struct auth_domain *, struct module *);

View File

@ -2459,6 +2459,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
case -EHOSTUNREACH:
case -EADDRINUSE:
case -ENOBUFS:
case -ENOTCONN:
break;
default:
printk("%s: connect returned unhandled error %d\n",