Commit 603c05a1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client fixes from Trond Myklebust:

 - Fix another deadlock involving nfs_release_folio()

 - localio:
     - Stop I/O upon hitting a fatal error
     - Deal with page offsets that are > PAGE_SIZE

 - Fix size read races in truncate, fallocate and copy offload

 - Several bugfixes for the NFSv4.x directory delegation client code

 - pNFS:
    - Fix a deadlock when returning delegations during open
    - Fix memory leaks in various error paths

* tag 'nfs-for-6.19-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: Fix size read races in truncate, fallocate and copy offload
  NFS: Don't immediately return directory delegations when disabled
  NFS/localio: Deal with page bases that are > PAGE_SIZE
  NFS/localio: Stop further I/O upon hitting an error
  NFSv4.x: Directory delegations don't require any state recovery
  NFSv4: Don't free slots prematurely if requesting a directory delegation
  NFSv4: Fix nfs_clear_verifier_delegated() for delegated directories
  NFS: Fix directory delegation verifier checks
  pnfs/blocklayout: Fix memory leak in bl_parse_scsi()
  pnfs/flexfiles: Fix memory leak in nfs4_ff_alloc_deviceid_node()
  NFS: Fix a deadlock involving nfs_release_folio()
  pNFS: Fix a deadlock when returning a delegation during open()
parents bc08b658 d5811e62
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -417,8 +417,10 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
	d->map = bl_map_simple;
	d->pr_key = v->scsi.pr_key;

	if (d->len == 0)
		return -ENODEV;
	if (d->len == 0) {
		error = -ENODEV;
		goto out_blkdev_put;
	}

	ops = bdev->bd_disk->fops->pr_ops;
	if (!ops) {
+6 −1
Original line number Diff line number Diff line
@@ -149,7 +149,7 @@ static int nfs4_do_check_delegation(struct inode *inode, fmode_t type,
int nfs4_have_delegation(struct inode *inode, fmode_t type, int flags)
{
	if (S_ISDIR(inode->i_mode) && !directory_delegations)
		nfs_inode_evict_delegation(inode);
		nfs4_inode_set_return_delegation_on_close(inode);
	return nfs4_do_check_delegation(inode, type, flags, true);
}

@@ -581,6 +581,10 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
	if (delegation == NULL)
		return 0;

	/* Directory delegations don't require any state recovery */
	if (!S_ISREG(inode->i_mode))
		goto out_return;

	if (!issync)
		mode |= O_NONBLOCK;
	/* Recall of any remaining application leases */
@@ -604,6 +608,7 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
		goto out;
	}

out_return:
	err = nfs_do_return_delegation(inode, delegation, issync);
out:
	/* Refcount matched in nfs_start_delegation_return_locked() */
+51 −27
Original line number Diff line number Diff line
@@ -1440,7 +1440,8 @@ static void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)

	if (!dir || !nfs_verify_change_attribute(dir, verf))
		return;
	if (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
	if (NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0) ||
	    (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0)))
		nfs_set_verifier_delegated(&verf);
	dentry->d_time = verf;
}
@@ -1465,6 +1466,49 @@ void nfs_set_verifier(struct dentry *dentry, unsigned long verf)
EXPORT_SYMBOL_GPL(nfs_set_verifier);

#if IS_ENABLED(CONFIG_NFS_V4)
static void nfs_clear_verifier_file(struct inode *inode)
{
	struct dentry *alias;
	struct inode *dir;

	hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
		spin_lock(&alias->d_lock);
		dir = d_inode_rcu(alias->d_parent);
		if (!dir ||
		    !NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0))
			nfs_unset_verifier_delegated(&alias->d_time);
		spin_unlock(&alias->d_lock);
	}
}

static void nfs_clear_verifier_directory(struct inode *dir)
{
	struct dentry *this_parent;
	struct dentry *dentry;
	struct inode *inode;

	if (hlist_empty(&dir->i_dentry))
		return;
	this_parent =
		hlist_entry(dir->i_dentry.first, struct dentry, d_u.d_alias);

	spin_lock(&this_parent->d_lock);
	nfs_unset_verifier_delegated(&this_parent->d_time);
	dentry = d_first_child(this_parent);
	hlist_for_each_entry_from(dentry, d_sib) {
		if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR))
			continue;
		inode = d_inode_rcu(dentry);
		if (inode &&
		    NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
			continue;
		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
		nfs_unset_verifier_delegated(&dentry->d_time);
		spin_unlock(&dentry->d_lock);
	}
	spin_unlock(&this_parent->d_lock);
}

/**
 * nfs_clear_verifier_delegated - clear the dir verifier delegation tag
 * @inode: pointer to inode
@@ -1477,16 +1521,13 @@ EXPORT_SYMBOL_GPL(nfs_set_verifier);
 */
void nfs_clear_verifier_delegated(struct inode *inode)
{
	struct dentry *alias;

	if (!inode)
		return;
	spin_lock(&inode->i_lock);
	hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
		spin_lock(&alias->d_lock);
		nfs_unset_verifier_delegated(&alias->d_time);
		spin_unlock(&alias->d_lock);
	}
	if (S_ISREG(inode->i_mode))
		nfs_clear_verifier_file(inode);
	else if (S_ISDIR(inode->i_mode))
		nfs_clear_verifier_directory(inode);
	spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_clear_verifier_delegated);
@@ -1516,14 +1557,6 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
	if (!nfs_dentry_verify_change(dir, dentry))
		return 0;

	/*
	 * If we have a directory delegation then we don't need to revalidate
	 * the directory. The delegation will either get recalled or we will
	 * receive a notification when it changes.
	 */
	if (nfs_have_directory_delegation(dir))
		return 0;

	/* Revalidate nfsi->cache_change_attribute before we declare a match */
	if (nfs_mapping_need_revalidate_inode(dir)) {
		if (rcu_walk)
@@ -2216,13 +2249,6 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
}
EXPORT_SYMBOL_GPL(nfs_atomic_open);

static int
nfs_lookup_revalidate_delegated_parent(struct inode *dir, struct dentry *dentry,
				       struct inode *inode)
{
	return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
}

static int
nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
		       struct dentry *dentry, unsigned int flags)
@@ -2247,12 +2273,10 @@ nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
	if (inode == NULL)
		goto full_reval;

	if (nfs_verifier_is_delegated(dentry))
	if (nfs_verifier_is_delegated(dentry) ||
	    nfs_have_directory_delegation(inode))
		return nfs_lookup_revalidate_delegated(dir, dentry, inode);

	if (nfs_have_directory_delegation(dir))
		return nfs_lookup_revalidate_delegated_parent(dir, dentry, inode);

	/* NFS only supports OPEN on regular files */
	if (!S_ISREG(inode->i_mode))
		goto full_reval;
+2 −1
Original line number Diff line number Diff line
@@ -511,7 +511,8 @@ static bool nfs_release_folio(struct folio *folio, gfp_t gfp)
		if ((current_gfp_context(gfp) & GFP_KERNEL) != GFP_KERNEL ||
		    current_is_kswapd() || current_is_kcompactd())
			return false;
		if (nfs_wb_folio(folio->mapping->host, folio) < 0)
		if (nfs_wb_folio_reclaim(folio->mapping->host, folio) < 0 ||
		    folio_test_private(folio))
			return false;
	}
	return nfs_fscache_release_folio(folio, gfp);
+1 −1
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
			      sizeof(struct nfs4_ff_ds_version),
			      gfp_flags);
	if (!ds_versions)
		goto out_scratch;
		goto out_err_drain_dsaddrs;

	for (i = 0; i < version_count; i++) {
		/* 20 = version(4) + minor_version(4) + rsize(4) + wsize(4) +
Loading