summaryrefslogtreecommitdiff
path: root/fs/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/callback.c4
-rw-r--r--fs/nfs/callback.h3
-rw-r--r--fs/nfs/client.c29
-rw-r--r--fs/nfs/delegation.c8
-rw-r--r--fs/nfs/delegation.h13
-rw-r--r--fs/nfs/dir.c53
-rw-r--r--fs/nfs/inode.c23
-rw-r--r--fs/nfs/internal.h3
-rw-r--r--fs/nfs/localio.c247
-rw-r--r--fs/nfs/namespace.c16
-rw-r--r--fs/nfs/nfs3client.c14
-rw-r--r--fs/nfs/nfs3proc.c3
-rw-r--r--fs/nfs/nfs4client.c41
-rw-r--r--fs/nfs/nfs4file.c2
-rw-r--r--fs/nfs/nfs4idmap.c7
-rw-r--r--fs/nfs/nfs4proc.c120
-rw-r--r--fs/nfs/nfs4trace.h1
-rw-r--r--fs/nfs/nfs4xdr.c106
-rw-r--r--fs/nfs/pnfs.c3
-rw-r--r--fs/nfs/pnfs_nfs.c66
-rw-r--r--fs/nfs/proc.c3
-rw-r--r--fs/nfs/super.c33
-rw-r--r--fs/nfs/sysfs.c1
-rw-r--r--fs/nfs/unlink.c3
24 files changed, 550 insertions, 252 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index c8b837006bb2..fabda0f6ec1a 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -258,7 +258,7 @@ err_start:
/*
* Kill the callback thread if it's no longer being used.
*/
-void nfs_callback_down(int minorversion, struct net *net)
+void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt)
{
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
struct svc_serv *serv;
@@ -270,7 +270,7 @@ void nfs_callback_down(int minorversion, struct net *net)
if (cb_info->users == 0) {
svc_set_num_threads(serv, NULL, 0);
dprintk("nfs_callback_down: service destroyed\n");
- svc_destroy(&cb_info->serv);
+ xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv);
}
mutex_unlock(&nfs_callback_mutex);
}
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index 154a6ed1299f..8809f93d82c0 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -188,7 +188,8 @@ extern __be32 nfs4_callback_recall(void *argp, void *resp,
struct cb_process_state *cps);
#if IS_ENABLED(CONFIG_NFS_V4)
extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
-extern void nfs_callback_down(int minorversion, struct net *net);
+extern void nfs_callback_down(int minorversion, struct net *net,
+ struct rpc_xprt *xprt);
#endif /* CONFIG_NFS_V4 */
/*
* nfs41: Callbacks are expected to not cause substantial latency,
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 4e3dcc157a83..2aaea9c98c2c 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -338,6 +338,14 @@ again:
/* Match the xprt security policy */
if (clp->cl_xprtsec.policy != data->xprtsec.policy)
continue;
+ if (clp->cl_xprtsec.policy == RPC_XPRTSEC_TLS_X509) {
+ if (clp->cl_xprtsec.cert_serial !=
+ data->xprtsec.cert_serial)
+ continue;
+ if (clp->cl_xprtsec.privkey_serial !=
+ data->xprtsec.privkey_serial)
+ continue;
+ }
refcount_inc(&clp->cl_count);
return clp;
@@ -776,10 +784,18 @@ static int nfs_init_server(struct nfs_server *server,
server->fattr_valid = NFS_ATTR_FATTR_V4;
}
- if (ctx->rsize)
+ if (ctx->bsize) {
+ server->bsize = ctx->bsize;
+ server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_BSIZE;
+ }
+ if (ctx->rsize) {
server->rsize = nfs_io_size(ctx->rsize, clp->cl_proto);
- if (ctx->wsize)
+ server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_RSIZE;
+ }
+ if (ctx->wsize) {
server->wsize = nfs_io_size(ctx->wsize, clp->cl_proto);
+ server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_WSIZE;
+ }
server->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ;
@@ -969,8 +985,13 @@ EXPORT_SYMBOL_GPL(nfs_probe_server);
void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
{
target->flags = source->flags;
- target->rsize = source->rsize;
- target->wsize = source->wsize;
+ target->automount_inherit = source->automount_inherit;
+ if (source->automount_inherit & NFS_AUTOMOUNT_INHERIT_BSIZE)
+ target->bsize = source->bsize;
+ if (source->automount_inherit & NFS_AUTOMOUNT_INHERIT_RSIZE)
+ target->rsize = source->rsize;
+ if (source->automount_inherit & NFS_AUTOMOUNT_INHERIT_WSIZE)
+ target->wsize = source->wsize;
target->acregmin = source->acregmin;
target->acregmax = source->acregmax;
target->acdirmin = source->acdirmin;
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 9d3a5f29f17f..2248e3ad089a 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -30,6 +30,11 @@
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
+bool directory_delegations = true;
+module_param(directory_delegations, bool, 0644);
+MODULE_PARM_DESC(directory_delegations,
+ "Enable the use of directory delegations, defaults to on.");
+
static struct hlist_head *nfs_delegation_hash(struct nfs_server *server,
const struct nfs_fh *fhandle)
{
@@ -143,6 +148,8 @@ 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);
return nfs4_do_check_delegation(inode, type, flags, true);
}
@@ -379,6 +386,7 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
delegation->inode = NULL;
rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock);
+ clear_bit(NFS_INO_REQ_DIR_DELEG, &nfsi->flags);
return delegation;
}
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 08ec2e9c68a4..46d866adb5c2 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -124,6 +124,19 @@ static inline int nfs_have_delegated_mtime(struct inode *inode)
NFS_DELEGATION_FLAG_TIME);
}
+extern bool directory_delegations;
+
+static inline void nfs_request_directory_delegation(struct inode *inode)
+{
+ if (S_ISDIR(inode->i_mode))
+ set_bit(NFS_INO_REQ_DIR_DELEG, &NFS_I(inode)->flags);
+}
+
+static inline bool nfs_have_directory_delegation(struct inode *inode)
+{
+ return S_ISDIR(inode->i_mode) && nfs_have_delegated_attributes(inode);
+}
+
int nfs4_delegation_hash_alloc(struct nfs_server *server);
#endif
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 46d9c65d50f8..23a78a742b61 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -789,16 +789,17 @@ again:
goto out;
}
+ nfs_set_verifier(dentry, dir_verifier);
inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
alias = d_splice_alias(inode, dentry);
d_lookup_done(dentry);
if (alias) {
if (IS_ERR(alias))
goto out;
+ nfs_set_verifier(alias, dir_verifier);
dput(dentry);
dentry = alias;
}
- nfs_set_verifier(dentry, dir_verifier);
trace_nfs_readdir_lookup(d_inode(parent), dentry, 0);
out:
dput(dentry);
@@ -1514,6 +1515,15 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
return 0;
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)
@@ -1894,13 +1904,15 @@ static int nfs_dentry_delete(const struct dentry *dentry)
}
/* Ensure that we revalidate inode->i_nlink */
-static void nfs_drop_nlink(struct inode *inode)
+static void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
spin_lock(&inode->i_lock);
/* drop the inode if we're reasonably sure this is the last link */
- if (inode->i_nlink > 0)
+ if (inode->i_nlink > 0 && gencount == nfsi->attr_gencount)
drop_nlink(inode);
- NFS_I(inode)->attr_gencount = nfs_inc_attr_generation_counter();
+ nfsi->attr_gencount = nfs_inc_attr_generation_counter();
nfs_set_cache_invalid(
inode, NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
NFS_INO_INVALID_NLINK);
@@ -1914,8 +1926,9 @@ static void nfs_drop_nlink(struct inode *inode)
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+ unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
nfs_complete_unlink(dentry, inode);
- nfs_drop_nlink(inode);
+ nfs_drop_nlink(inode, gencount);
}
iput(inode);
}
@@ -1991,13 +2004,14 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
nfs_lookup_advise_force_readdirplus(dir, flags);
no_entry:
+ nfs_set_verifier(dentry, dir_verifier);
res = d_splice_alias(inode, dentry);
if (res != NULL) {
if (IS_ERR(res))
goto out;
+ nfs_set_verifier(res, dir_verifier);
dentry = res;
}
- nfs_set_verifier(dentry, dir_verifier);
out:
trace_nfs_lookup_exit(dir, dentry, flags, PTR_ERR_OR_ZERO(res));
nfs_free_fattr(fattr);
@@ -2139,12 +2153,12 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
d_drop(dentry);
switch (err) {
case -ENOENT:
- d_splice_alias(NULL, dentry);
if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
dir_verifier = inode_peek_iversion_raw(dir);
else
dir_verifier = nfs_save_change_attribute(dir);
nfs_set_verifier(dentry, dir_verifier);
+ d_splice_alias(NULL, dentry);
break;
case -EISDIR:
case -ENOTDIR:
@@ -2203,6 +2217,13 @@ no_open:
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)
{
@@ -2229,6 +2250,9 @@ nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
if (nfs_verifier_is_delegated(dentry))
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;
@@ -2268,11 +2292,12 @@ int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry,
return -ENAMETOOLONG;
if (open_flags & O_CREAT) {
- file->f_mode |= FMODE_CREATED;
error = nfs_do_create(dir, dentry, mode, open_flags);
- if (error)
+ if (!error) {
+ file->f_mode |= FMODE_CREATED;
+ return finish_open(file, dentry, NULL);
+ } else if (error != -EEXIST || open_flags & O_EXCL)
return error;
- return finish_open(file, dentry, NULL);
}
if (d_in_lookup(dentry)) {
/* The only flags nfs_lookup considers are
@@ -2506,9 +2531,11 @@ static int nfs_safe_remove(struct dentry *dentry)
trace_nfs_remove_enter(dir, dentry);
if (inode != NULL) {
+ unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
+
error = NFS_PROTO(dir)->remove(dir, dentry);
if (error == 0)
- nfs_drop_nlink(inode);
+ nfs_drop_nlink(inode, gencount);
} else
error = NFS_PROTO(dir)->remove(dir, dentry);
if (error == -ENOENT)
@@ -2708,6 +2735,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
{
struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry);
+ unsigned long new_gencount = 0;
struct dentry *dentry = NULL;
struct rpc_task *task;
bool must_unblock = false;
@@ -2760,6 +2788,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
} else {
block_revalidate(new_dentry);
must_unblock = true;
+ new_gencount = NFS_I(new_inode)->attr_gencount;
spin_unlock(&new_dentry->d_lock);
}
@@ -2799,7 +2828,7 @@ out:
new_dir, new_dentry, error);
if (!error) {
if (new_inode != NULL)
- nfs_drop_nlink(new_inode);
+ nfs_drop_nlink(new_inode, new_gencount);
/*
* The d_move() should be here instead of in an async RPC completion
* handler because we need the proper locks to move the dentry. If
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 18b57c7c2f97..84049f3cd340 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -475,7 +475,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
goto out_no_inode;
}
- if (inode->i_state & I_NEW) {
+ if (inode_state_read_once(inode) & I_NEW) {
struct nfs_inode *nfsi = NFS_I(inode);
unsigned long now = jiffies;
@@ -718,6 +718,8 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct nfs_fattr *fattr;
loff_t oldsize = i_size_read(inode);
int error = 0;
+ kuid_t task_uid = current_fsuid();
+ kuid_t owner_uid = inode->i_uid;
nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
@@ -739,9 +741,11 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (nfs_have_delegated_mtime(inode) && attr->ia_valid & ATTR_MTIME) {
spin_lock(&inode->i_lock);
if (attr->ia_valid & ATTR_MTIME_SET) {
- nfs_set_timestamps_to_ts(inode, attr);
- attr->ia_valid &= ~(ATTR_MTIME|ATTR_MTIME_SET|
+ if (uid_eq(task_uid, owner_uid)) {
+ nfs_set_timestamps_to_ts(inode, attr);
+ attr->ia_valid &= ~(ATTR_MTIME|ATTR_MTIME_SET|
ATTR_ATIME|ATTR_ATIME_SET);
+ }
} else {
nfs_update_timestamps(inode, attr->ia_valid);
attr->ia_valid &= ~(ATTR_MTIME|ATTR_ATIME);
@@ -751,10 +755,12 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
attr->ia_valid & ATTR_ATIME &&
!(attr->ia_valid & ATTR_MTIME)) {
if (attr->ia_valid & ATTR_ATIME_SET) {
- spin_lock(&inode->i_lock);
- nfs_set_timestamps_to_ts(inode, attr);
- spin_unlock(&inode->i_lock);
- attr->ia_valid &= ~(ATTR_ATIME|ATTR_ATIME_SET);
+ if (uid_eq(task_uid, owner_uid)) {
+ spin_lock(&inode->i_lock);
+ nfs_set_timestamps_to_ts(inode, attr);
+ spin_unlock(&inode->i_lock);
+ attr->ia_valid &= ~(ATTR_ATIME|ATTR_ATIME_SET);
+ }
} else {
nfs_update_delegated_atime(inode);
attr->ia_valid &= ~ATTR_ATIME;
@@ -1383,6 +1389,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
status = pnfs_sync_inode(inode, false);
if (status)
goto out;
+ } else if (nfs_have_directory_delegation(inode)) {
+ status = 0;
+ goto out;
}
status = -ENOMEM;
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 2ecd38e1d17a..2e596244799f 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -13,7 +13,7 @@
#include <linux/nfslocalio.h>
#include <linux/wait_bit.h>
-#define NFS_SB_MASK (SB_NOSUID|SB_NODEV|SB_NOEXEC|SB_SYNCHRONOUS)
+#define NFS_SB_MASK (SB_RDONLY|SB_NOSUID|SB_NODEV|SB_NOEXEC|SB_SYNCHRONOUS)
extern const struct export_operations nfs_export_ops;
@@ -152,7 +152,6 @@ struct nfs_fs_context {
struct super_block *sb;
struct dentry *dentry;
struct nfs_fattr *fattr;
- unsigned int inherited_bsize;
} clone_data;
};
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 2c0455e91571..a113bfdacfd6 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -42,10 +42,9 @@ struct nfs_local_kiocb {
/* Begin mostly DIO-specific members */
size_t end_len;
short int end_iter_index;
- short int n_iters;
- bool iter_is_dio_aligned[NFSLOCAL_MAX_IOS];
- loff_t offset[NFSLOCAL_MAX_IOS] ____cacheline_aligned;
+ atomic_t n_iters;
struct iov_iter iters[NFSLOCAL_MAX_IOS];
+ bool iter_is_dio_aligned[NFSLOCAL_MAX_IOS];
/* End mostly DIO-specific members */
};
@@ -314,7 +313,9 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
init_sync_kiocb(&iocb->kiocb, file);
iocb->hdr = hdr;
+ iocb->kiocb.ki_pos = hdr->args.offset;
iocb->kiocb.ki_flags &= ~IOCB_APPEND;
+ iocb->kiocb.ki_complete = NULL;
iocb->aio_complete_work = NULL;
iocb->end_iter_index = -1;
@@ -338,8 +339,6 @@ nfs_is_local_dio_possible(struct nfs_local_kiocb *iocb, int rw,
if (unlikely(!nf_dio_mem_align || !nf_dio_offset_align))
return false;
- if (unlikely(nf_dio_offset_align > PAGE_SIZE))
- return false;
if (unlikely(len < nf_dio_offset_align))
return false;
@@ -388,13 +387,24 @@ static bool nfs_iov_iter_aligned_bvec(const struct iov_iter *i,
return true;
}
+static void
+nfs_local_iter_setup(struct iov_iter *iter, int rw, struct bio_vec *bvec,
+ unsigned int nvecs, unsigned long total,
+ size_t start, size_t len)
+{
+ iov_iter_bvec(iter, rw, bvec, nvecs, total);
+ if (start)
+ iov_iter_advance(iter, start);
+ iov_iter_truncate(iter, len);
+}
+
/*
* Setup as many as 3 iov_iter based on extents described by @local_dio.
* Returns the number of iov_iter that were setup.
*/
static int
nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
- unsigned int nvecs, size_t len,
+ unsigned int nvecs, unsigned long total,
struct nfs_local_dio *local_dio)
{
int n_iters = 0;
@@ -402,39 +412,17 @@ nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
/* Setup misaligned start? */
if (local_dio->start_len) {
- iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len);
- iters[n_iters].count = local_dio->start_len;
- iocb->offset[n_iters] = iocb->hdr->args.offset;
- iocb->iter_is_dio_aligned[n_iters] = false;
- ++n_iters;
- }
-
- /* Setup misaligned end?
- * If so, the end is purposely setup to be issued using buffered IO
- * before the middle (which will use DIO, if DIO-aligned, with AIO).
- * This creates problems if/when the end results in a partial write.
- * So must save index and length of end to handle this corner case.
- */
- if (local_dio->end_len) {
- iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len);
- iocb->offset[n_iters] = local_dio->end_offset;
- iov_iter_advance(&iters[n_iters],
- local_dio->start_len + local_dio->middle_len);
- iocb->iter_is_dio_aligned[n_iters] = false;
- /* Save index and length of end */
- iocb->end_iter_index = n_iters;
- iocb->end_len = local_dio->end_len;
+ nfs_local_iter_setup(&iters[n_iters], rw, iocb->bvec,
+ nvecs, total, 0, local_dio->start_len);
++n_iters;
}
- /* Setup DIO-aligned middle to be issued last, to allow for
- * DIO with AIO completion (see nfs_local_call_{read,write}).
+ /*
+ * Setup DIO-aligned middle, if there is no misaligned end (below)
+ * then AIO completion is used, see nfs_local_call_{read,write}
*/
- iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len);
- if (local_dio->start_len)
- iov_iter_advance(&iters[n_iters], local_dio->start_len);
- iters[n_iters].count -= local_dio->end_len;
- iocb->offset[n_iters] = local_dio->middle_offset;
+ nfs_local_iter_setup(&iters[n_iters], rw, iocb->bvec, nvecs,
+ total, local_dio->start_len, local_dio->middle_len);
iocb->iter_is_dio_aligned[n_iters] =
nfs_iov_iter_aligned_bvec(&iters[n_iters],
@@ -442,12 +430,22 @@ nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
if (unlikely(!iocb->iter_is_dio_aligned[n_iters])) {
trace_nfs_local_dio_misaligned(iocb->hdr->inode,
- iocb->hdr->args.offset, len, local_dio);
+ local_dio->start_len, local_dio->middle_len, local_dio);
return 0; /* no DIO-aligned IO possible */
}
+ iocb->end_iter_index = n_iters;
++n_iters;
- iocb->n_iters = n_iters;
+ /* Setup misaligned end? */
+ if (local_dio->end_len) {
+ nfs_local_iter_setup(&iters[n_iters], rw, iocb->bvec,
+ nvecs, total, local_dio->start_len +
+ local_dio->middle_len, local_dio->end_len);
+ iocb->end_iter_index = n_iters;
+ ++n_iters;
+ }
+
+ atomic_set(&iocb->n_iters, n_iters);
return n_iters;
}
@@ -473,18 +471,26 @@ nfs_local_iters_init(struct nfs_local_kiocb *iocb, int rw)
}
len = hdr->args.count - total;
+ /*
+ * For each iocb, iocb->n_iters is always at least 1 and we always
+ * end io after first nfs_local_pgio_done call unless misaligned DIO.
+ */
+ atomic_set(&iocb->n_iters, 1);
+
if (test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) {
struct nfs_local_dio local_dio;
if (nfs_is_local_dio_possible(iocb, rw, len, &local_dio) &&
- nfs_local_iters_setup_dio(iocb, rw, v, len, &local_dio) != 0)
+ nfs_local_iters_setup_dio(iocb, rw, v, len, &local_dio) != 0) {
+ /* Ensure DIO WRITE's IO on stable storage upon completion */
+ if (rw == ITER_SOURCE)
+ iocb->kiocb.ki_flags |= IOCB_DSYNC|IOCB_SYNC;
return; /* is DIO-aligned */
+ }
}
/* Use buffered IO */
- iocb->offset[0] = hdr->args.offset;
iov_iter_bvec(&iocb->iters[0], rw, iocb->bvec, v, len);
- iocb->n_iters = 1;
}
static void
@@ -504,9 +510,11 @@ nfs_local_pgio_init(struct nfs_pgio_header *hdr,
hdr->task.tk_start = ktime_get();
}
-static void
-nfs_local_pgio_done(struct nfs_pgio_header *hdr, long status)
+static bool
+nfs_local_pgio_done(struct nfs_local_kiocb *iocb, long status, bool force)
{
+ struct nfs_pgio_header *hdr = iocb->hdr;
+
/* Must handle partial completions */
if (status >= 0) {
hdr->res.count += status;
@@ -517,6 +525,12 @@ nfs_local_pgio_done(struct nfs_pgio_header *hdr, long status)
hdr->res.op_status = nfs_localio_errno_to_nfs4_stat(status);
hdr->task.tk_status = status;
}
+
+ if (force)
+ return true;
+
+ BUG_ON(atomic_read(&iocb->n_iters) <= 0);
+ return atomic_dec_and_test(&iocb->n_iters);
}
static void
@@ -547,11 +561,11 @@ static inline void nfs_local_pgio_aio_complete(struct nfs_local_kiocb *iocb)
queue_work(nfsiod_workqueue, &iocb->work);
}
-static void
-nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
+static void nfs_local_read_done(struct nfs_local_kiocb *iocb)
{
struct nfs_pgio_header *hdr = iocb->hdr;
struct file *filp = iocb->kiocb.ki_filp;
+ long status = hdr->task.tk_status;
if ((iocb->kiocb.ki_flags & IOCB_DIRECT) && status == -EINVAL) {
/* Underlying FS will return -EINVAL if misaligned DIO is attempted. */
@@ -564,20 +578,27 @@ nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
*/
hdr->res.replen = 0;
- if (hdr->res.count != hdr->args.count ||
- hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp)))
+ /* nfs_readpage_result() handles short read */
+
+ if (hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp)))
hdr->res.eof = true;
dprintk("%s: read %ld bytes eof %d.\n", __func__,
status > 0 ? status : 0, hdr->res.eof);
}
+static inline void nfs_local_read_iocb_done(struct nfs_local_kiocb *iocb)
+{
+ nfs_local_read_done(iocb);
+ nfs_local_pgio_release(iocb);
+}
+
static void nfs_local_read_aio_complete_work(struct work_struct *work)
{
struct nfs_local_kiocb *iocb =
container_of(work, struct nfs_local_kiocb, work);
- nfs_local_pgio_release(iocb);
+ nfs_local_read_iocb_done(iocb);
}
static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret)
@@ -585,8 +606,10 @@ static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret)
struct nfs_local_kiocb *iocb =
container_of(kiocb, struct nfs_local_kiocb, kiocb);
- nfs_local_pgio_done(iocb->hdr, ret);
- nfs_local_read_done(iocb, ret);
+ /* AIO completion of DIO read should always be last to complete */
+ if (unlikely(!nfs_local_pgio_done(iocb, ret, false)))
+ return;
+
nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_read_aio_complete_work */
}
@@ -595,33 +618,34 @@ static void nfs_local_call_read(struct work_struct *work)
struct nfs_local_kiocb *iocb =
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
- const struct cred *save_cred;
+ bool force_done = false;
ssize_t status;
+ int n_iters;
- save_cred = override_creds(filp->f_cred);
-
- for (int i = 0; i < iocb->n_iters ; i++) {
+ n_iters = atomic_read(&iocb->n_iters);
+ for (int i = 0; i < n_iters ; i++) {
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
- iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
- iocb->aio_complete_work = nfs_local_read_aio_complete_work;
- }
+ /* Only use AIO completion if DIO-aligned segment is last */
+ if (i == iocb->end_iter_index) {
+ iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
+ iocb->aio_complete_work = nfs_local_read_aio_complete_work;
+ }
+ } else
+ iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+
+ scoped_with_creds(filp->f_cred)
+ status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
- iocb->kiocb.ki_pos = iocb->offset[i];
- status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
if (status != -EIOCBQUEUED) {
- nfs_local_pgio_done(iocb->hdr, status);
- if (iocb->hdr->task.tk_status)
+ if (unlikely(status >= 0 && status < iocb->iters[i].count))
+ force_done = true; /* Partial read */
+ if (nfs_local_pgio_done(iocb, status, force_done)) {
+ nfs_local_read_iocb_done(iocb);
break;
+ }
}
}
-
- revert_creds(save_cred);
-
- if (status != -EIOCBQUEUED) {
- nfs_local_read_done(iocb, status);
- nfs_local_pgio_release(iocb);
- }
}
static int
@@ -736,11 +760,10 @@ static void nfs_local_vfs_getattr(struct nfs_local_kiocb *iocb)
fattr->du.nfs3.used = stat.blocks << 9;
}
-static void
-nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
+static void nfs_local_write_done(struct nfs_local_kiocb *iocb)
{
struct nfs_pgio_header *hdr = iocb->hdr;
- struct inode *inode = hdr->inode;
+ long status = hdr->task.tk_status;
dprintk("%s: wrote %ld bytes.\n", __func__, status > 0 ? status : 0);
@@ -759,10 +782,17 @@ nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
nfs_set_pgio_error(hdr, -ENOSPC, hdr->args.offset);
status = -ENOSPC;
/* record -ENOSPC in terms of nfs_local_pgio_done */
- nfs_local_pgio_done(hdr, status);
+ (void) nfs_local_pgio_done(iocb, status, true);
}
if (hdr->task.tk_status < 0)
- nfs_reset_boot_verifier(inode);
+ nfs_reset_boot_verifier(hdr->inode);
+}
+
+static inline void nfs_local_write_iocb_done(struct nfs_local_kiocb *iocb)
+{
+ nfs_local_write_done(iocb);
+ nfs_local_vfs_getattr(iocb);
+ nfs_local_pgio_release(iocb);
}
static void nfs_local_write_aio_complete_work(struct work_struct *work)
@@ -770,8 +800,7 @@ static void nfs_local_write_aio_complete_work(struct work_struct *work)
struct nfs_local_kiocb *iocb =
container_of(work, struct nfs_local_kiocb, work);
- nfs_local_vfs_getattr(iocb);
- nfs_local_pgio_release(iocb);
+ nfs_local_write_iocb_done(iocb);
}
static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret)
@@ -779,8 +808,10 @@ static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret)
struct nfs_local_kiocb *iocb =
container_of(kiocb, struct nfs_local_kiocb, kiocb);
- nfs_local_pgio_done(iocb->hdr, ret);
- nfs_local_write_done(iocb, ret);
+ /* AIO completion of DIO write should always be last to complete */
+ if (unlikely(!nfs_local_pgio_done(iocb, ret, false)))
+ return;
+
nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_write_aio_complete_work */
}
@@ -790,64 +821,40 @@ static void nfs_local_call_write(struct work_struct *work)
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
unsigned long old_flags = current->flags;
- const struct cred *save_cred;
+ bool force_done = false;
ssize_t status;
+ int n_iters;
current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
- save_cred = override_creds(filp->f_cred);
file_start_write(filp);
- for (int i = 0; i < iocb->n_iters ; i++) {
+ n_iters = atomic_read(&iocb->n_iters);
+ for (int i = 0; i < n_iters ; i++) {
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
- iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
- iocb->aio_complete_work = nfs_local_write_aio_complete_work;
- }
-retry:
- iocb->kiocb.ki_pos = iocb->offset[i];
- status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
- if (status != -EIOCBQUEUED) {
- if (unlikely(status >= 0 && status < iocb->iters[i].count)) {
- /* partial write */
- if (i == iocb->end_iter_index) {
- /* Must not account partial end, otherwise, due
- * to end being issued before middle: the partial
- * write accounting in nfs_local_write_done()
- * would incorrectly advance hdr->args.offset
- */
- status = 0;
- } else {
- /* Partial write at start or buffered middle,
- * exit early.
- */
- nfs_local_pgio_done(iocb->hdr, status);
- break;
- }
- } else if (unlikely(status == -ENOTBLK &&
- (iocb->kiocb.ki_flags & IOCB_DIRECT))) {
- /* VFS will return -ENOTBLK if DIO WRITE fails to
- * invalidate the page cache. Retry using buffered IO.
- */
- iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
- iocb->kiocb.ki_complete = NULL;
- iocb->aio_complete_work = NULL;
- goto retry;
+ /* Only use AIO completion if DIO-aligned segment is last */
+ if (i == iocb->end_iter_index) {
+ iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
+ iocb->aio_complete_work = nfs_local_write_aio_complete_work;
}
- nfs_local_pgio_done(iocb->hdr, status);
- if (iocb->hdr->task.tk_status)
+ } else
+ iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+
+ scoped_with_creds(filp->f_cred)
+ status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
+
+ if (status != -EIOCBQUEUED) {
+ if (unlikely(status >= 0 && status < iocb->iters[i].count))
+ force_done = true; /* Partial write */
+ if (nfs_local_pgio_done(iocb, status, force_done)) {
+ nfs_local_write_iocb_done(iocb);
break;
+ }
}
}
file_end_write(filp);
- revert_creds(save_cred);
current->flags = old_flags;
-
- if (status != -EIOCBQUEUED) {
- nfs_local_write_done(iocb, status);
- nfs_local_vfs_getattr(iocb);
- nfs_local_pgio_release(iocb);
- }
}
static int
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 5a4d193da1a9..af9be0c5f516 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -149,6 +149,7 @@ struct vfsmount *nfs_d_automount(struct path *path)
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct nfs_server *server = NFS_SB(path->dentry->d_sb);
struct nfs_client *client = server->nfs_client;
+ unsigned long s_flags = path->dentry->d_sb->s_flags;
int timeout = READ_ONCE(nfs_mountpoint_expiry_timeout);
int ret;
@@ -169,11 +170,21 @@ struct vfsmount *nfs_d_automount(struct path *path)
if (!ctx->clone_data.fattr)
goto out_fc;
+ if (fc->cred != server->cred) {
+ put_cred(fc->cred);
+ fc->cred = get_cred(server->cred);
+ }
+
if (fc->net_ns != client->cl_net) {
put_net(fc->net_ns);
fc->net_ns = get_net(client->cl_net);
}
+ /* Inherit the flags covered by NFS_SB_MASK */
+ fc->sb_flags_mask |= NFS_SB_MASK;
+ fc->sb_flags &= ~NFS_SB_MASK;
+ fc->sb_flags |= s_flags & NFS_SB_MASK;
+
/* for submounts we want the same server; referrals will reassign */
memcpy(&ctx->nfs_server._address, &client->cl_addr, client->cl_addrlen);
ctx->nfs_server.addrlen = client->cl_addrlen;
@@ -184,6 +195,10 @@ struct vfsmount *nfs_d_automount(struct path *path)
ctx->nfs_mod = client->cl_nfs_mod;
get_nfs_version(ctx->nfs_mod);
+ /* Inherit block sizes if they were specified as mount parameters */
+ if (server->automount_inherit & NFS_AUTOMOUNT_INHERIT_BSIZE)
+ ctx->bsize = server->bsize;
+
ret = client->rpc_ops->submount(fc, server);
if (ret < 0) {
mnt = ERR_PTR(ret);
@@ -283,7 +298,6 @@ int nfs_do_submount(struct fs_context *fc)
return -ENOMEM;
ctx->internal = true;
- ctx->clone_data.inherited_bsize = ctx->clone_data.sb->s_blocksize_bits;
p = nfs_devname(dentry, buffer, 4096);
if (IS_ERR(p)) {
diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c
index 0d7310c1ee0c..5d97c1d38bb6 100644
--- a/fs/nfs/nfs3client.c
+++ b/fs/nfs/nfs3client.c
@@ -2,6 +2,7 @@
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/sunrpc/addr.h>
+#include <net/handshake.h>
#include "internal.h"
#include "nfs3_fs.h"
#include "netns.h"
@@ -98,7 +99,11 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
.net = mds_clp->cl_net,
.timeparms = &ds_timeout,
.cred = mds_srv->cred,
- .xprtsec = mds_clp->cl_xprtsec,
+ .xprtsec = {
+ .policy = RPC_XPRTSEC_NONE,
+ .cert_serial = TLS_NO_CERT,
+ .privkey_serial = TLS_NO_PRIVKEY,
+ },
.connect_timeout = connect_timeout,
.reconnect_timeout = connect_timeout,
};
@@ -111,9 +116,14 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
cl_init.hostname = buf;
switch (ds_proto) {
+ case XPRT_TRANSPORT_TCP_TLS:
+ if (mds_clp->cl_xprtsec.policy != RPC_XPRTSEC_NONE)
+ cl_init.xprtsec = mds_clp->cl_xprtsec;
+ else
+ ds_proto = XPRT_TRANSPORT_TCP;
+ fallthrough;
case XPRT_TRANSPORT_RDMA:
case XPRT_TRANSPORT_TCP:
- case XPRT_TRANSPORT_TCP_TLS:
if (mds_clp->cl_nconnect > 1)
cl_init.nconnect = mds_clp->cl_nconnect;
}
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index a4cb67573aa7..1181f9cc6dbd 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -483,7 +483,8 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void
nfs3_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct inode *same_parent)
{
msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME];
}
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 5998d6bd8a4f..96bccefbe2cb 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -11,6 +11,7 @@
#include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/bc_xprt.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
+#include <net/handshake.h>
#include "internal.h"
#include "callback.h"
#include "delegation.h"
@@ -280,8 +281,13 @@ error:
*/
static void nfs4_destroy_callback(struct nfs_client *clp)
{
- if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
- nfs_callback_down(clp->cl_mvops->minor_version, clp->cl_net);
+ if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) {
+ struct rpc_xprt *xprt;
+
+ xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
+ nfs_callback_down(clp->cl_mvops->minor_version, clp->cl_net,
+ xprt);
+ }
}
static void nfs4_shutdown_client(struct nfs_client *clp)
@@ -983,7 +989,11 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
.net = mds_clp->cl_net,
.timeparms = &ds_timeout,
.cred = mds_srv->cred,
- .xprtsec = mds_srv->nfs_client->cl_xprtsec,
+ .xprtsec = {
+ .policy = RPC_XPRTSEC_NONE,
+ .cert_serial = TLS_NO_CERT,
+ .privkey_serial = TLS_NO_PRIVKEY,
+ },
};
char buf[INET6_ADDRSTRLEN + 1];
@@ -992,9 +1002,14 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
cl_init.hostname = buf;
switch (ds_proto) {
+ case XPRT_TRANSPORT_TCP_TLS:
+ if (mds_srv->nfs_client->cl_xprtsec.policy != RPC_XPRTSEC_NONE)
+ cl_init.xprtsec = mds_srv->nfs_client->cl_xprtsec;
+ else
+ ds_proto = XPRT_TRANSPORT_TCP;
+ fallthrough;
case XPRT_TRANSPORT_RDMA:
case XPRT_TRANSPORT_TCP:
- case XPRT_TRANSPORT_TCP_TLS:
if (mds_clp->cl_nconnect > 1) {
cl_init.nconnect = mds_clp->cl_nconnect;
cl_init.max_connect = NFS_MAX_TRANSPORTS;
@@ -1164,10 +1179,20 @@ static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
if (error < 0)
return error;
- if (ctx->rsize)
- server->rsize = nfs_io_size(ctx->rsize, server->nfs_client->cl_proto);
- if (ctx->wsize)
- server->wsize = nfs_io_size(ctx->wsize, server->nfs_client->cl_proto);
+ if (ctx->bsize) {
+ server->bsize = ctx->bsize;
+ server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_BSIZE;
+ }
+ if (ctx->rsize) {
+ server->rsize =
+ nfs_io_size(ctx->rsize, server->nfs_client->cl_proto);
+ server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_RSIZE;
+ }
+ if (ctx->wsize) {
+ server->wsize =
+ nfs_io_size(ctx->wsize, server->nfs_client->cl_proto);
+ server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_WSIZE;
+ }
server->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ;
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 7f43e890d356..7317f26892c5 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -431,6 +431,8 @@ void nfs42_ssc_unregister_ops(void)
static int nfs4_setlease(struct file *file, int arg, struct file_lease **lease,
void **priv)
{
+ if (!S_ISREG(file_inode(file)->i_mode))
+ return -EINVAL;
return nfs4_proc_setlease(file, arg, lease, priv);
}
diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c
index 00932500fce4..9e1c48c5c0b8 100644
--- a/fs/nfs/nfs4idmap.c
+++ b/fs/nfs/nfs4idmap.c
@@ -306,15 +306,12 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
const char *type, void *data,
size_t data_size, struct idmap *idmap)
{
- const struct cred *saved_cred;
struct key *rkey;
const struct user_key_payload *payload;
ssize_t ret;
- saved_cred = override_creds(id_resolver_cache);
- rkey = nfs_idmap_request_key(name, namelen, type, idmap);
- revert_creds(saved_cred);
-
+ scoped_with_creds(id_resolver_cache)
+ rkey = nfs_idmap_request_key(name, namelen, type, idmap);
if (IS_ERR(rkey)) {
ret = PTR_ERR(rkey);
goto out;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 411776718494..ec1ce593dea2 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1780,8 +1780,17 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
if (nfs_stateid_is_sequential(state, stateid))
break;
- if (status)
- break;
+ if (status) {
+ if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
+ !nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
+ trace_nfs4_open_stateid_update_skip(state->inode,
+ stateid, status);
+ return;
+ } else {
+ break;
+ }
+ }
+
/* Rely on seqids for serialisation with NFSv4.0 */
if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
break;
@@ -3174,18 +3183,6 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
if (opendata->o_res.rflags & NFS4_OPEN_RESULT_PRESERVE_UNLINKED)
set_bit(NFS_INO_PRESERVE_UNLINKED, &NFS_I(state->inode)->flags);
- dentry = opendata->dentry;
- if (d_really_is_negative(dentry)) {
- struct dentry *alias;
- d_drop(dentry);
- alias = d_splice_alias(igrab(state->inode), dentry);
- /* d_splice_alias() can't fail here - it's a non-directory */
- if (alias) {
- dput(ctx->dentry);
- ctx->dentry = dentry = alias;
- }
- }
-
switch(opendata->o_arg.claim) {
default:
break;
@@ -3196,7 +3193,20 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
break;
if (opendata->o_res.delegation.type != 0)
dir_verifier = nfs_save_change_attribute(dir);
- nfs_set_verifier(dentry, dir_verifier);
+ }
+
+ dentry = opendata->dentry;
+ nfs_set_verifier(dentry, dir_verifier);
+ if (d_really_is_negative(dentry)) {
+ struct dentry *alias;
+ d_drop(dentry);
+ alias = d_splice_alias(igrab(state->inode), dentry);
+ /* d_splice_alias() can't fail here - it's a non-directory */
+ if (alias) {
+ dput(ctx->dentry);
+ nfs_set_verifier(alias, dir_verifier);
+ ctx->dentry = dentry = alias;
+ }
}
/* Parse layoutget results before we check for access */
@@ -4460,6 +4470,30 @@ out:
return status;
}
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+static bool should_request_dir_deleg(struct inode *inode)
+{
+ if (!directory_delegations)
+ return false;
+ if (!inode)
+ return false;
+ if (!S_ISDIR(inode->i_mode))
+ return false;
+ if (!nfs_server_capable(inode, NFS_CAP_DIR_DELEG))
+ return false;
+ if (!test_and_clear_bit(NFS_INO_REQ_DIR_DELEG, &(NFS_I(inode)->flags)))
+ return false;
+ if (nfs4_have_delegation(inode, FMODE_READ, 0))
+ return false;
+ return true;
+}
+#else
+static bool should_request_dir_deleg(struct inode *inode)
+{
+ return false;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr, struct inode *inode)
{
@@ -4477,7 +4511,9 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_argp = &args,
.rpc_resp = &res,
};
+ struct nfs4_gdd_res gdd_res;
unsigned short task_flags = 0;
+ int status;
if (nfs4_has_session(server->nfs_client))
task_flags = RPC_TASK_MOVEABLE;
@@ -4486,11 +4522,31 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
task_flags |= RPC_TASK_TIMEOUT;
+ args.get_dir_deleg = should_request_dir_deleg(inode);
+ if (args.get_dir_deleg)
+ res.gdd_res = &gdd_res;
+
nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, fattr->label), inode, 0);
nfs_fattr_init(fattr);
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
- return nfs4_do_call_sync(server->client, server, &msg,
- &args.seq_args, &res.seq_res, task_flags);
+
+ status = nfs4_do_call_sync(server->client, server, &msg,
+ &args.seq_args, &res.seq_res, task_flags);
+ if (args.get_dir_deleg) {
+ switch (status) {
+ case 0:
+ if (gdd_res.status != GDD4_OK)
+ break;
+ status = nfs_inode_set_delegation(
+ inode, current_cred(), FMODE_READ,
+ &gdd_res.deleg, 0, NFS4_OPEN_DELEGATE_READ);
+ break;
+ case -ENOTSUPP:
+ case -EOPNOTSUPP:
+ server->caps &= ~NFS_CAP_DIR_DELEG;
+ }
+ }
+ return status;
}
int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -4503,8 +4559,14 @@ int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
do {
err = _nfs4_proc_getattr(server, fhandle, fattr, inode);
trace_nfs4_getattr(server, fhandle, fattr, err);
- err = nfs4_handle_exception(server, err,
- &exception);
+ switch (err) {
+ default:
+ err = nfs4_handle_exception(server, err, &exception);
+ break;
+ case -ENOTSUPP:
+ case -EOPNOTSUPP:
+ exception.retry = true;
+ }
} while (exception.retry);
return err;
}
@@ -4715,16 +4777,19 @@ static int _nfs4_proc_lookupp(struct inode *inode,
};
unsigned short task_flags = 0;
- if (NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL)
+ if (server->flags & NFS_MOUNT_SOFTREVAL)
task_flags |= RPC_TASK_TIMEOUT;
+ if (server->caps & NFS_CAP_MOVEABLE)
+ task_flags |= RPC_TASK_MOVEABLE;
args.bitmask = nfs4_bitmask(server, fattr->label);
nfs_fattr_init(fattr);
+ nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
dprintk("NFS call lookupp ino=0x%lx\n", inode->i_ino);
- status = nfs4_call_sync(clnt, server, &msg, &args.seq_args,
- &res.seq_res, task_flags);
+ status = nfs4_do_call_sync(clnt, server, &msg, &args.seq_args,
+ &res.seq_res, task_flags);
dprintk("NFS reply lookupp: %d\n", status);
return status;
}
@@ -4765,6 +4830,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
int status = 0;
if (!nfs4_have_delegation(inode, FMODE_READ, 0)) {
+ nfs_request_directory_delegation(inode);
res.fattr = nfs_alloc_fattr();
if (res.fattr == NULL)
return -ENOMEM;
@@ -4872,6 +4938,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
+ nfs_request_directory_delegation(dir);
+
if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
sattr->ia_mode &= ~current_umask();
state = nfs4_do_open(dir, ctx, flags, sattr, ilabel, NULL);
@@ -4968,6 +5036,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg,
nfs4_init_sequence(&args->seq_args, &res->seq_res, 1, 0);
nfs_fattr_init(res->dir_attr);
+ nfs_request_directory_delegation(d_inode(dentry->d_parent));
if (inode) {
nfs4_inode_return_delegation(inode);
@@ -5002,7 +5071,8 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void nfs4_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct inode *same_parent)
{
struct nfs_renameargs *arg = msg->rpc_argp;
struct nfs_renameres *res = msg->rpc_resp;
@@ -5013,6 +5083,8 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg,
nfs4_inode_make_writeable(old_inode);
if (new_inode)
nfs4_inode_return_delegation(new_inode);
+ if (same_parent)
+ nfs_request_directory_delegation(same_parent);
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
res->server = NFS_SB(old_dentry->d_sb);
nfs4_init_sequence(&arg->seq_args, &res->seq_res, 1, 0);
@@ -10819,6 +10891,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
.minor_version = 1,
.init_caps = NFS_CAP_READDIRPLUS
| NFS_CAP_ATOMIC_OPEN
+ | NFS_CAP_DIR_DELEG
| NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41
| NFS_CAP_ATOMIC_OPEN_V1
@@ -10845,6 +10918,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
.minor_version = 2,
.init_caps = NFS_CAP_READDIRPLUS
| NFS_CAP_ATOMIC_OPEN
+ | NFS_CAP_DIR_DELEG
| NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41
| NFS_CAP_ATOMIC_OPEN_V1
diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h
index 9776d220cec3..6285128e631a 100644
--- a/fs/nfs/nfs4trace.h
+++ b/fs/nfs/nfs4trace.h
@@ -1353,6 +1353,7 @@ DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_setattr);
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_delegreturn);
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update);
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update_wait);
+DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update_skip);
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_close_stateid_update_wait);
DECLARE_EVENT_CLASS(nfs4_getattr_event,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 1d0e6c10f921..b6fe30577fab 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -393,6 +393,20 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5)
#define encode_reclaim_complete_maxsz (op_encode_hdr_maxsz + 4)
#define decode_reclaim_complete_maxsz (op_decode_hdr_maxsz + 4)
+#define encode_get_dir_deleg_maxsz (op_encode_hdr_maxsz + \
+ 4 /* gdda_signal_deleg_avail */ + \
+ 8 /* gdda_notification_types */ + \
+ nfstime4_maxsz /* gdda_child_attr_delay */ + \
+ nfstime4_maxsz /* gdda_dir_attr_delay */ + \
+ nfs4_fattr_bitmap_maxsz /* gdda_child_attributes */ + \
+ nfs4_fattr_bitmap_maxsz /* gdda_dir_attributes */)
+#define decode_get_dir_deleg_maxsz (op_decode_hdr_maxsz + \
+ 4 /* gddrnf_status */ + \
+ encode_verifier_maxsz /* gddr_cookieverf */ + \
+ encode_stateid_maxsz /* gddr_stateid */ + \
+ 8 /* gddr_notification */ + \
+ nfs4_fattr_maxsz /* gddr_child_attributes */ + \
+ nfs4_fattr_maxsz /* gddr_dir_attributes */)
#define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
1 /* layout type */ + \
@@ -444,6 +458,8 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#else /* CONFIG_NFS_V4_1 */
#define encode_sequence_maxsz 0
#define decode_sequence_maxsz 0
+#define encode_get_dir_deleg_maxsz 0
+#define decode_get_dir_deleg_maxsz 0
#define encode_layoutreturn_maxsz 0
#define decode_layoutreturn_maxsz 0
#define encode_layoutget_maxsz 0
@@ -631,11 +647,13 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
+ encode_get_dir_deleg_maxsz + \
encode_getattr_maxsz + \
encode_renew_maxsz)
#define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
+ decode_get_dir_deleg_maxsz + \
decode_getattr_maxsz + \
decode_renew_maxsz)
#define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \
@@ -2008,6 +2026,33 @@ static void encode_sequence(struct xdr_stream *xdr,
#ifdef CONFIG_NFS_V4_1
static void
+encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
+{
+ struct timespec64 ts = { 0, 0 };
+ u32 notifications[1] = { 0 };
+ u32 attributes[1] = { 0 };
+ __be32 *p;
+
+ encode_op_hdr(xdr, OP_GET_DIR_DELEGATION, decode_get_dir_deleg_maxsz, hdr);
+
+ /* We don't handle CB_RECALLABLE_OBJ_AVAIL yet. */
+ xdr_stream_encode_bool(xdr, false);
+
+ xdr_encode_bitmap4(xdr, notifications, ARRAY_SIZE(notifications));
+
+ /* Request no delay on attribute updates */
+ p = reserve_space(xdr, 12 + 12);
+ p = xdr_encode_nfstime4(p, &ts);
+ xdr_encode_nfstime4(p, &ts);
+
+ /* Requested child attributes */
+ xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+
+ /* Requested dir attributes */
+ xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+}
+
+static void
encode_getdeviceinfo(struct xdr_stream *xdr,
const struct nfs4_getdeviceinfo_args *args,
struct compound_hdr *hdr)
@@ -2143,6 +2188,11 @@ static void encode_free_stateid(struct xdr_stream *xdr,
}
#else
static inline void
+encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
+{
+}
+
+static inline void
encode_layoutreturn(struct xdr_stream *xdr,
const struct nfs4_layoutreturn_args *args,
struct compound_hdr *hdr)
@@ -2356,6 +2406,8 @@ static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
+ if (args->get_dir_deleg)
+ encode_get_dir_delegation(xdr, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
}
@@ -5994,6 +6046,49 @@ static int decode_layout_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
return decode_stateid(xdr, stateid);
}
+static int decode_get_dir_delegation(struct xdr_stream *xdr,
+ struct nfs4_getattr_res *res)
+{
+ struct nfs4_gdd_res *gdd_res = res->gdd_res;
+ nfs4_verifier cookieverf;
+ u32 bitmap[1];
+ int status;
+
+ status = decode_op_hdr(xdr, OP_GET_DIR_DELEGATION);
+ if (status)
+ return status;
+
+ if (xdr_stream_decode_u32(xdr, &gdd_res->status))
+ return -EIO;
+
+ if (gdd_res->status == GDD4_UNAVAIL)
+ return xdr_inline_decode(xdr, 4) ? 0 : -EIO;
+
+ status = decode_verifier(xdr, &cookieverf);
+ if (status)
+ return status;
+
+ status = decode_delegation_stateid(xdr, &gdd_res->deleg);
+ if (status)
+ return status;
+
+ /* Decode supported notification types. */
+ status = decode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
+ if (status < 0)
+ return status;
+
+ /* Decode supported child attributes. */
+ status = decode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
+ if (status < 0)
+ return status;
+
+ /* Decode supported attributes. */
+ status = decode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
+ if (status < 0)
+ return status;
+ return 0;
+}
+
static int decode_getdeviceinfo(struct xdr_stream *xdr,
struct nfs4_getdeviceinfo_res *res)
{
@@ -6208,6 +6303,12 @@ static int decode_free_stateid(struct xdr_stream *xdr,
return res->status;
}
#else
+static int decode_get_dir_delegation(struct xdr_stream *xdr,
+ struct nfs4_getattr_res *res)
+{
+ return 0;
+}
+
static inline
int decode_layoutreturn(struct xdr_stream *xdr,
struct nfs4_layoutreturn_res *res)
@@ -6525,6 +6626,11 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
status = decode_putfh(xdr);
if (status)
goto out;
+ if (res->gdd_res) {
+ status = decode_get_dir_delegation(xdr, res);
+ if (status)
+ goto out;
+ }
status = decode_getfattr(xdr, res->fattr, res->server);
out:
return status;
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index a3135b5af7ee..b72d7cc36766 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -317,7 +317,7 @@ pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo)
WARN_ONCE(1, "NFS: BUG unfreed layout segments.\n");
pnfs_detach_layout_hdr(lo);
/* Notify pnfs_destroy_layout_final() that we're done */
- if (inode->i_state & (I_FREEING | I_CLEAR))
+ if (inode_state_read(inode) & (I_FREEING | I_CLEAR))
wake_up_var_locked(lo, &inode->i_lock);
spin_unlock(&inode->i_lock);
pnfs_free_layout_hdr(lo);
@@ -464,6 +464,7 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
struct pnfs_layout_segment *lseg, *next;
set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
+ clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(lo->plh_inode)->flags);
list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
pnfs_clear_lseg_state(lseg, lseg_list);
pnfs_clear_layoutreturn_info(lo);
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index 7b32afb29782..9976cc16b689 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -809,8 +809,11 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
unsigned int retrans)
{
struct nfs_client *clp = ERR_PTR(-EIO);
+ struct nfs_client *mds_clp = mds_srv->nfs_client;
+ enum xprtsec_policies xprtsec_policy = mds_clp->cl_xprtsec.policy;
struct nfs4_pnfs_ds_addr *da;
unsigned long connect_timeout = timeo * (retrans + 1) * HZ / 10;
+ int ds_proto;
int status = 0;
dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
@@ -834,27 +837,28 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
.xprtsec = clp->cl_xprtsec,
};
- if (da->da_transport != clp->cl_proto &&
- clp->cl_proto != XPRT_TRANSPORT_TCP_TLS)
- continue;
- if (da->da_transport == XPRT_TRANSPORT_TCP &&
- mds_srv->nfs_client->cl_proto == XPRT_TRANSPORT_TCP_TLS)
+ if (xprt_args.ident == XPRT_TRANSPORT_TCP &&
+ clp->cl_proto == XPRT_TRANSPORT_TCP_TLS)
xprt_args.ident = XPRT_TRANSPORT_TCP_TLS;
- if (da->da_addr.ss_family != clp->cl_addr.ss_family)
+ if (xprt_args.ident != clp->cl_proto)
+ continue;
+ if (xprt_args.dstaddr->sa_family !=
+ clp->cl_addr.ss_family)
continue;
/* Add this address as an alias */
rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
- rpc_clnt_test_and_add_xprt, NULL);
+ rpc_clnt_test_and_add_xprt, NULL);
continue;
}
- if (da->da_transport == XPRT_TRANSPORT_TCP &&
- mds_srv->nfs_client->cl_proto == XPRT_TRANSPORT_TCP_TLS)
- da->da_transport = XPRT_TRANSPORT_TCP_TLS;
- clp = get_v3_ds_connect(mds_srv,
- &da->da_addr,
- da->da_addrlen, da->da_transport,
- timeo, retrans);
+
+ ds_proto = da->da_transport;
+ if (ds_proto == XPRT_TRANSPORT_TCP &&
+ xprtsec_policy != RPC_XPRTSEC_NONE)
+ ds_proto = XPRT_TRANSPORT_TCP_TLS;
+
+ clp = get_v3_ds_connect(mds_srv, &da->da_addr, da->da_addrlen,
+ ds_proto, timeo, retrans);
if (IS_ERR(clp))
continue;
clp->cl_rpcclient->cl_softerr = 0;
@@ -880,7 +884,10 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
u32 minor_version)
{
struct nfs_client *clp = ERR_PTR(-EIO);
+ struct nfs_client *mds_clp = mds_srv->nfs_client;
+ enum xprtsec_policies xprtsec_policy = mds_clp->cl_xprtsec.policy;
struct nfs4_pnfs_ds_addr *da;
+ int ds_proto;
int status = 0;
dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
@@ -908,12 +915,8 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
.data = &xprtdata,
};
- if (da->da_transport != clp->cl_proto &&
- clp->cl_proto != XPRT_TRANSPORT_TCP_TLS)
- continue;
- if (da->da_transport == XPRT_TRANSPORT_TCP &&
- mds_srv->nfs_client->cl_proto ==
- XPRT_TRANSPORT_TCP_TLS) {
+ if (xprt_args.ident == XPRT_TRANSPORT_TCP &&
+ clp->cl_proto == XPRT_TRANSPORT_TCP_TLS) {
struct sockaddr *addr =
(struct sockaddr *)&da->da_addr;
struct sockaddr_in *sin =
@@ -944,7 +947,10 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
xprt_args.ident = XPRT_TRANSPORT_TCP_TLS;
xprt_args.servername = servername;
}
- if (da->da_addr.ss_family != clp->cl_addr.ss_family)
+ if (xprt_args.ident != clp->cl_proto)
+ continue;
+ if (xprt_args.dstaddr->sa_family !=
+ clp->cl_addr.ss_family)
continue;
/**
@@ -958,15 +964,14 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
if (xprtdata.cred)
put_cred(xprtdata.cred);
} else {
- if (da->da_transport == XPRT_TRANSPORT_TCP &&
- mds_srv->nfs_client->cl_proto ==
- XPRT_TRANSPORT_TCP_TLS)
- da->da_transport = XPRT_TRANSPORT_TCP_TLS;
- clp = nfs4_set_ds_client(mds_srv,
- &da->da_addr,
- da->da_addrlen,
- da->da_transport, timeo,
- retrans, minor_version);
+ ds_proto = da->da_transport;
+ if (ds_proto == XPRT_TRANSPORT_TCP &&
+ xprtsec_policy != RPC_XPRTSEC_NONE)
+ ds_proto = XPRT_TRANSPORT_TCP_TLS;
+
+ clp = nfs4_set_ds_client(mds_srv, &da->da_addr,
+ da->da_addrlen, ds_proto,
+ timeo, retrans, minor_version);
if (IS_ERR(clp))
continue;
@@ -977,7 +982,6 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
clp = ERR_PTR(-EIO);
continue;
}
-
}
}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 63e71310b9f6..39df80e4ae6f 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -353,7 +353,8 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void
nfs_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct inode *same_parent)
{
msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME];
}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 72dee6f3050e..57d372db03b9 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1052,16 +1052,6 @@ int nfs_reconfigure(struct fs_context *fc)
sync_filesystem(sb);
/*
- * The SB_RDONLY flag has been removed from the superblock during
- * mounts to prevent interference between different filesystems.
- * Similarly, it is also necessary to ignore the SB_RDONLY flag
- * during reconfiguration; otherwise, it may also result in the
- * creation of redundant superblocks when mounting a directory with
- * different rw and ro flags multiple times.
- */
- fc->sb_flags_mask &= ~SB_RDONLY;
-
- /*
* Userspace mount programs that send binary options generally send
* them populated with default values. We have no way to know which
* ones were explicitly specified. Fall back to legacy behavior and
@@ -1101,8 +1091,9 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx)
sb->s_blocksize = 0;
sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr;
sb->s_op = server->nfs_client->cl_nfs_mod->sops;
- if (ctx->bsize)
- sb->s_blocksize = nfs_block_size(ctx->bsize, &sb->s_blocksize_bits);
+ if (server->bsize)
+ sb->s_blocksize =
+ nfs_block_size(server->bsize, &sb->s_blocksize_bits);
switch (server->nfs_client->rpc_ops->version) {
case 2:
@@ -1318,26 +1309,13 @@ int nfs_get_tree_common(struct fs_context *fc)
if (IS_ERR(server))
return PTR_ERR(server);
- /*
- * When NFS_MOUNT_UNSHARED is not set, NFS forces the sharing of a
- * superblock among each filesystem that mounts sub-directories
- * belonging to a single exported root path.
- * To prevent interference between different filesystems, the
- * SB_RDONLY flag should be removed from the superblock.
- */
if (server->flags & NFS_MOUNT_UNSHARED)
compare_super = NULL;
- else
- fc->sb_flags &= ~SB_RDONLY;
/* -o noac implies -o sync */
if (server->flags & NFS_MOUNT_NOAC)
fc->sb_flags |= SB_SYNCHRONOUS;
- if (ctx->clone_data.sb)
- if (ctx->clone_data.sb->s_flags & SB_SYNCHRONOUS)
- fc->sb_flags |= SB_SYNCHRONOUS;
-
/* Get a superblock - note that we may end up sharing one that already exists */
fc->s_fs_info = server;
s = sget_fc(fc, compare_super, nfs_set_super);
@@ -1361,13 +1339,8 @@ int nfs_get_tree_common(struct fs_context *fc)
}
if (!s->s_root) {
- unsigned bsize = ctx->clone_data.inherited_bsize;
/* initial superblock/root creation */
nfs_fill_super(s, ctx);
- if (bsize) {
- s->s_blocksize_bits = bsize;
- s->s_blocksize = 1U << bsize;
- }
error = nfs_get_cache_cookie(s, ctx);
if (error < 0)
goto error_splat_super;
diff --git a/fs/nfs/sysfs.c b/fs/nfs/sysfs.c
index 545148d42dcc..ea6e6168092b 100644
--- a/fs/nfs/sysfs.c
+++ b/fs/nfs/sysfs.c
@@ -189,6 +189,7 @@ static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent,
return p;
kobject_put(&p->kobject);
+ kobject_put(&p->nfs_net_kobj);
}
return NULL;
}
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index b55467911648..4db818c0f9dd 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -390,7 +390,8 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
nfs_sb_active(old_dir->i_sb);
- NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dentry, new_dentry);
+ NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dentry, new_dentry,
+ old_dir == new_dir ? old_dir : NULL);
return rpc_run_task(&task_setup_data);
}