Commit fd88e181 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client fixes from Trond Myklebust:
 "Bugfixes:
   - NFSv4.2: Fix a memory leak in nfs4_set_security_label
   - NFSv2/v3: abort nfs_atomic_open_v23 if the name is too long.
   - NFS: Add appropriate memory barriers to the sillyrename code
   - Propagate readlink errors in nfs_symlink_filler
   - NFS: don't invalidate dentries on transient errors
   - NFS: fix unnecessary synchronous writes in random write workloads
   - NFSv4.1: enforce rootpath check when deciding whether or not to trunk

  Other:
   - Change email address for Trond Myklebust due to email server concerns"

* tag 'nfs-for-6.10-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: add barriers when testing for NFS_FSDATA_BLOCKED
  SUNRPC: return proper error from gss_wrap_req_priv
  NFSv4.1 enforce rootpath check in fs_location query
  NFS: abort nfs_atomic_open_v23 if name is too long.
  nfs: don't invalidate dentries on transient errors
  nfs: Avoid flushing many pages with NFS_FILE_SYNC
  nfs: propagate readlink errors in nfs_symlink_filler
  MAINTAINERS: Change email address for Trond Myklebust
  NFSv4: Fix memory leak in nfs4_set_security_label
parents 3572597c 99bc9f2e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -15825,7 +15825,7 @@ F: drivers/nfc/virtual_ncidev.c
F:	tools/testing/selftests/nci/
NFS, SUNRPC, AND LOCKD CLIENTS
M:	Trond Myklebust <trond.myklebust@hammerspace.com>
M:	Trond Myklebust <trondmy@kernel.org>
M:	Anna Schumaker <anna@kernel.org>
L:	linux-nfs@vger.kernel.org
S:	Maintained
+48 −29
Original line number Diff line number Diff line
@@ -1627,7 +1627,16 @@ nfs_lookup_revalidate_done(struct inode *dir, struct dentry *dentry,
	switch (error) {
	case 1:
		break;
	case 0:
	case -ETIMEDOUT:
		if (inode && (IS_ROOT(dentry) ||
			      NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL))
			error = 1;
		break;
	case -ESTALE:
	case -ENOENT:
		error = 0;
		fallthrough;
	default:
		/*
		 * We can't d_drop the root of a disconnected tree:
		 * its d_hash is on the s_anon list and d_drop() would hide
@@ -1682,18 +1691,8 @@ static int nfs_lookup_revalidate_dentry(struct inode *dir,

	dir_verifier = nfs_save_change_attribute(dir);
	ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
	if (ret < 0) {
		switch (ret) {
		case -ESTALE:
		case -ENOENT:
			ret = 0;
			break;
		case -ETIMEDOUT:
			if (NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL)
				ret = 1;
		}
	if (ret < 0)
		goto out;
	}

	/* Request help from readdirplus */
	nfs_lookup_advise_force_readdirplus(dir, flags);
@@ -1737,7 +1736,7 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
			 unsigned int flags)
{
	struct inode *inode;
	int error;
	int error = 0;

	nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
	inode = d_inode(dentry);
@@ -1782,7 +1781,7 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
out_bad:
	if (flags & LOOKUP_RCU)
		return -ECHILD;
	return nfs_lookup_revalidate_done(dir, dentry, inode, 0);
	return nfs_lookup_revalidate_done(dir, dentry, inode, error);
}

static int
@@ -1804,9 +1803,10 @@ __nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
		if (parent != READ_ONCE(dentry->d_parent))
			return -ECHILD;
	} else {
		/* Wait for unlink to complete */
		/* Wait for unlink to complete - see unblock_revalidate() */
		wait_var_event(&dentry->d_fsdata,
			       dentry->d_fsdata != NFS_FSDATA_BLOCKED);
			       smp_load_acquire(&dentry->d_fsdata)
			       != NFS_FSDATA_BLOCKED);
		parent = dget_parent(dentry);
		ret = reval(d_inode(parent), dentry, flags);
		dput(parent);
@@ -1819,6 +1819,29 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
	return __nfs_lookup_revalidate(dentry, flags, nfs_do_lookup_revalidate);
}

static void block_revalidate(struct dentry *dentry)
{
	/* old devname - just in case */
	kfree(dentry->d_fsdata);

	/* Any new reference that could lead to an open
	 * will take ->d_lock in lookup_open() -> d_lookup().
	 * Holding this lock ensures we cannot race with
	 * __nfs_lookup_revalidate() and removes and need
	 * for further barriers.
	 */
	lockdep_assert_held(&dentry->d_lock);

	dentry->d_fsdata = NFS_FSDATA_BLOCKED;
}

static void unblock_revalidate(struct dentry *dentry)
{
	/* store_release ensures wait_var_event() sees the update */
	smp_store_release(&dentry->d_fsdata, NULL);
	wake_up_var(&dentry->d_fsdata);
}

/*
 * A weaker form of d_revalidate for revalidating just the d_inode(dentry)
 * when we don't really care about the dentry name. This is called when a
@@ -2255,6 +2278,9 @@ int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry,
	 */
	int error = 0;

	if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
		return -ENAMETOOLONG;

	if (open_flags & O_CREAT) {
		file->f_mode |= FMODE_CREATED;
		error = nfs_do_create(dir, dentry, mode, open_flags);
@@ -2549,15 +2575,12 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry)
		spin_unlock(&dentry->d_lock);
		goto out;
	}
	/* old devname */
	kfree(dentry->d_fsdata);
	dentry->d_fsdata = NFS_FSDATA_BLOCKED;
	block_revalidate(dentry);

	spin_unlock(&dentry->d_lock);
	error = nfs_safe_remove(dentry);
	nfs_dentry_remove_handle_error(dir, dentry, error);
	dentry->d_fsdata = NULL;
	wake_up_var(&dentry->d_fsdata);
	unblock_revalidate(dentry);
out:
	trace_nfs_unlink_exit(dir, dentry, error);
	return error;
@@ -2664,8 +2687,7 @@ nfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data)
{
	struct dentry *new_dentry = data->new_dentry;

	new_dentry->d_fsdata = NULL;
	wake_up_var(&new_dentry->d_fsdata);
	unblock_revalidate(new_dentry);
}

/*
@@ -2727,11 +2749,6 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
		if (WARN_ON(new_dentry->d_flags & DCACHE_NFSFS_RENAMED) ||
		    WARN_ON(new_dentry->d_fsdata == NFS_FSDATA_BLOCKED))
			goto out;
		if (new_dentry->d_fsdata) {
			/* old devname */
			kfree(new_dentry->d_fsdata);
			new_dentry->d_fsdata = NULL;
		}

		spin_lock(&new_dentry->d_lock);
		if (d_count(new_dentry) > 2) {
@@ -2753,7 +2770,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
			new_dentry = dentry;
			new_inode = NULL;
		} else {
			new_dentry->d_fsdata = NFS_FSDATA_BLOCKED;
			block_revalidate(new_dentry);
			must_unblock = true;
			spin_unlock(&new_dentry->d_lock);
		}
@@ -2765,6 +2782,8 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
	task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry,
				must_unblock ? nfs_unblock_rename : NULL);
	if (IS_ERR(task)) {
		if (must_unblock)
			unblock_revalidate(new_dentry);
		error = PTR_ERR(task);
		goto out;
	}
+23 −1
Original line number Diff line number Diff line
@@ -4023,6 +4023,23 @@ static void test_fs_location_for_trunking(struct nfs4_fs_location *location,
	}
}

static bool _is_same_nfs4_pathname(struct nfs4_pathname *path1,
				   struct nfs4_pathname *path2)
{
	int i;

	if (path1->ncomponents != path2->ncomponents)
		return false;
	for (i = 0; i < path1->ncomponents; i++) {
		if (path1->components[i].len != path2->components[i].len)
			return false;
		if (memcmp(path1->components[i].data, path2->components[i].data,
				path1->components[i].len))
			return false;
	}
	return true;
}

static int _nfs4_discover_trunking(struct nfs_server *server,
				   struct nfs_fh *fhandle)
{
@@ -4056,9 +4073,13 @@ static int _nfs4_discover_trunking(struct nfs_server *server,
	if (status)
		goto out_free_3;

	for (i = 0; i < locations->nlocations; i++)
	for (i = 0; i < locations->nlocations; i++) {
		if (!_is_same_nfs4_pathname(&locations->fs_path,
					&locations->locations[i].rootpath))
			continue;
		test_fs_location_for_trunking(&locations->locations[i], clp,
					      server);
	}
out_free_3:
	kfree(locations->fattr);
out_free_2:
@@ -6268,6 +6289,7 @@ nfs4_set_security_label(struct inode *inode, const void *buf, size_t buflen)
	if (status == 0)
		nfs_setsecurity(inode, fattr);

	nfs_free_fattr(fattr);
	return status;
}
#endif	/* CONFIG_NFS_V4_SECURITY_LABEL */
+5 −0
Original line number Diff line number Diff line
@@ -1545,6 +1545,11 @@ void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index)
					continue;
			} else if (index == prev->wb_index + 1)
				continue;
			/*
			 * We will submit more requests after these. Indicate
			 * this to the underlying layers.
			 */
			desc->pg_moreio = 1;
			nfs_pageio_complete(desc);
			break;
		}
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ static int nfs_symlink_filler(struct file *file, struct folio *folio)
error:
	folio_set_error(folio);
	folio_unlock(folio);
	return -EIO;
	return error;
}

static const char *nfs_get_link(struct dentry *dentry,
Loading