NFS client updates for Linux 6.19

Highlights include:
 
 Bugfixes:
 - Fix 'nlink' attribute update races when unlinking a file.
 - Add missing initialisers for the directory verifier in various places.
 - Don't regress the NFSv4 open state due to misordered racing replies.
 - Ensure the NFSv4.x callback server uses the correct transport
   connection.
 - Fix potential use-after-free races when shutting down the NFSv4.x
   callback server.
 - Fix a pNFS layout commit crash.
 - Assorted fixes to ensure correct propagation of mount options when the
   client crosses a filesystem boundary and triggers the VFS automount
   code.
 - More localio fixes.
 
 Features and cleanups:
 - Add initial support for basic directory delegations.
 - SunRPC back channel code cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQR8xgHcVzJNfOYElJo6EXfx2a6V0QUCaTrPnQAKCRA6EXfx2a6V
 0aLQAPwOs+bfoaPuk/EsC87m3rAtFJNPMg65toJY/6JnXnTGXgEAs1XlCcSCgc10
 bsX+D6QIudOoWExwFfLlVlFAFikGKQc=
 =DoZq
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-6.19-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client updates from Trond Myklebust:
 "Bugfixes:
   - Fix 'nlink' attribute update races when unlinking a file
   - Add missing initialisers for the directory verifier in various
     places
   - Don't regress the NFSv4 open state due to misordered racing replies
   - Ensure the NFSv4.x callback server uses the correct transport
     connection
   - Fix potential use-after-free races when shutting down the NFSv4.x
     callback server
   - Fix a pNFS layout commit crash
   - Assorted fixes to ensure correct propagation of mount options when
     the client crosses a filesystem boundary and triggers the VFS
     automount code
   - More localio fixes

  Features and cleanups:
   - Add initial support for basic directory delegations
   - SunRPC back channel code cleanups"

* tag 'nfs-for-6.19-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (24 commits)
  NFSv4: Handle NFS4ERR_NOTSUPP errors for directory delegations
  nfs/localio: remove 61 byte hole from needless ____cacheline_aligned
  nfs/localio: remove alignment size checking in nfs_is_local_dio_possible
  NFS: Fix up the automount fs_context to use the correct cred
  NFS: Fix inheritance of the block sizes when automounting
  NFS: Automounted filesystems should inherit ro,noexec,nodev,sync flags
  Revert "nfs: ignore SB_RDONLY when mounting nfs"
  Revert "nfs: clear SB_RDONLY before getting superblock"
  Revert "nfs: ignore SB_RDONLY when remounting nfs"
  NFS: Add a module option to disable directory delegations
  NFS: Shortcut lookup revalidations if we have a directory delegation
  NFS: Request a directory delegation during RENAME
  NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK
  NFS: Add support for sending GDD_GETATTR
  NFSv4/pNFS: Clear NFS_INO_LAYOUTCOMMIT in pnfs_mark_layout_stateid_invalid
  NFSv4.1: protect destroying and nullifying bc_serv structure
  SUNRPC: new helper function for stopping backchannel server
  SUNRPC: cleanup common code in backchannel request
  NFSv4.1: pass transport for callback shutdown
  NFSv4: ensure the open stateid seqid doesn't go backwards
  ...
pull/1354/merge
Linus Torvalds 2025-12-12 21:52:42 +12:00
commit 6bb34aff1e
25 changed files with 386 additions and 92 deletions

View File

@ -258,7 +258,7 @@ err_start:
/* /*
* Kill the callback thread if it's no longer being used. * 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 nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
struct svc_serv *serv; struct svc_serv *serv;
@ -270,7 +270,7 @@ void nfs_callback_down(int minorversion, struct net *net)
if (cb_info->users == 0) { if (cb_info->users == 0) {
svc_set_num_threads(serv, NULL, 0); svc_set_num_threads(serv, NULL, 0);
dprintk("nfs_callback_down: service destroyed\n"); 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); mutex_unlock(&nfs_callback_mutex);
} }

View File

@ -188,7 +188,8 @@ extern __be32 nfs4_callback_recall(void *argp, void *resp,
struct cb_process_state *cps); struct cb_process_state *cps);
#if IS_ENABLED(CONFIG_NFS_V4) #if IS_ENABLED(CONFIG_NFS_V4)
extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); 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 */ #endif /* CONFIG_NFS_V4 */
/* /*
* nfs41: Callbacks are expected to not cause substantial latency, * nfs41: Callbacks are expected to not cause substantial latency,

View File

@ -784,10 +784,18 @@ static int nfs_init_server(struct nfs_server *server,
server->fattr_valid = NFS_ATTR_FATTR_V4; 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); 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->wsize = nfs_io_size(ctx->wsize, clp->cl_proto);
server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_WSIZE;
}
server->acregmin = ctx->acregmin * HZ; server->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ; server->acregmax = ctx->acregmax * HZ;
@ -977,8 +985,13 @@ EXPORT_SYMBOL_GPL(nfs_probe_server);
void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
{ {
target->flags = source->flags; target->flags = source->flags;
target->rsize = source->rsize; target->automount_inherit = source->automount_inherit;
target->wsize = source->wsize; 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->acregmin = source->acregmin;
target->acregmax = source->acregmax; target->acregmax = source->acregmax;
target->acdirmin = source->acdirmin; target->acdirmin = source->acdirmin;

View File

@ -30,6 +30,11 @@
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK; static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644); 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, static struct hlist_head *nfs_delegation_hash(struct nfs_server *server,
const struct nfs_fh *fhandle) 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) 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); return nfs4_do_check_delegation(inode, type, flags, true);
} }
@ -379,6 +386,7 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
delegation->inode = NULL; delegation->inode = NULL;
rcu_assign_pointer(nfsi->delegation, NULL); rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
clear_bit(NFS_INO_REQ_DIR_DELEG, &nfsi->flags);
return delegation; return delegation;
} }

View File

@ -124,6 +124,19 @@ static inline int nfs_have_delegated_mtime(struct inode *inode)
NFS_DELEGATION_FLAG_TIME); 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); int nfs4_delegation_hash_alloc(struct nfs_server *server);
#endif #endif

View File

@ -789,16 +789,17 @@ again:
goto out; goto out;
} }
nfs_set_verifier(dentry, dir_verifier);
inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr); inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
alias = d_splice_alias(inode, dentry); alias = d_splice_alias(inode, dentry);
d_lookup_done(dentry); d_lookup_done(dentry);
if (alias) { if (alias) {
if (IS_ERR(alias)) if (IS_ERR(alias))
goto out; goto out;
nfs_set_verifier(alias, dir_verifier);
dput(dentry); dput(dentry);
dentry = alias; dentry = alias;
} }
nfs_set_verifier(dentry, dir_verifier);
trace_nfs_readdir_lookup(d_inode(parent), dentry, 0); trace_nfs_readdir_lookup(d_inode(parent), dentry, 0);
out: out:
dput(dentry); dput(dentry);
@ -1514,6 +1515,15 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
return 0; return 0;
if (!nfs_dentry_verify_change(dir, dentry)) if (!nfs_dentry_verify_change(dir, dentry))
return 0; 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 */ /* Revalidate nfsi->cache_change_attribute before we declare a match */
if (nfs_mapping_need_revalidate_inode(dir)) { if (nfs_mapping_need_revalidate_inode(dir)) {
if (rcu_walk) if (rcu_walk)
@ -1894,13 +1904,15 @@ static int nfs_dentry_delete(const struct dentry *dentry)
} }
/* Ensure that we revalidate inode->i_nlink */ /* 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); spin_lock(&inode->i_lock);
/* drop the inode if we're reasonably sure this is the last link */ /* 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); 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( nfs_set_cache_invalid(
inode, NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME | inode, NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
NFS_INO_INVALID_NLINK); 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) static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{ {
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
nfs_complete_unlink(dentry, inode); nfs_complete_unlink(dentry, inode);
nfs_drop_nlink(inode); nfs_drop_nlink(inode, gencount);
} }
iput(inode); 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); nfs_lookup_advise_force_readdirplus(dir, flags);
no_entry: no_entry:
nfs_set_verifier(dentry, dir_verifier);
res = d_splice_alias(inode, dentry); res = d_splice_alias(inode, dentry);
if (res != NULL) { if (res != NULL) {
if (IS_ERR(res)) if (IS_ERR(res))
goto out; goto out;
nfs_set_verifier(res, dir_verifier);
dentry = res; dentry = res;
} }
nfs_set_verifier(dentry, dir_verifier);
out: out:
trace_nfs_lookup_exit(dir, dentry, flags, PTR_ERR_OR_ZERO(res)); trace_nfs_lookup_exit(dir, dentry, flags, PTR_ERR_OR_ZERO(res));
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
@ -2139,12 +2153,12 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
d_drop(dentry); d_drop(dentry);
switch (err) { switch (err) {
case -ENOENT: case -ENOENT:
d_splice_alias(NULL, dentry);
if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE)) if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
dir_verifier = inode_peek_iversion_raw(dir); dir_verifier = inode_peek_iversion_raw(dir);
else else
dir_verifier = nfs_save_change_attribute(dir); dir_verifier = nfs_save_change_attribute(dir);
nfs_set_verifier(dentry, dir_verifier); nfs_set_verifier(dentry, dir_verifier);
d_splice_alias(NULL, dentry);
break; break;
case -EISDIR: case -EISDIR:
case -ENOTDIR: case -ENOTDIR:
@ -2202,6 +2216,13 @@ no_open:
} }
EXPORT_SYMBOL_GPL(nfs_atomic_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 static int
nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name, nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *dentry, unsigned int flags) 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)) if (nfs_verifier_is_delegated(dentry))
return nfs_lookup_revalidate_delegated(dir, dentry, 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 */ /* NFS only supports OPEN on regular files */
if (!S_ISREG(inode->i_mode)) if (!S_ISREG(inode->i_mode))
goto full_reval; goto full_reval;
@ -2507,9 +2531,11 @@ static int nfs_safe_remove(struct dentry *dentry)
trace_nfs_remove_enter(dir, dentry); trace_nfs_remove_enter(dir, dentry);
if (inode != NULL) { if (inode != NULL) {
unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
error = NFS_PROTO(dir)->remove(dir, dentry); error = NFS_PROTO(dir)->remove(dir, dentry);
if (error == 0) if (error == 0)
nfs_drop_nlink(inode); nfs_drop_nlink(inode, gencount);
} else } else
error = NFS_PROTO(dir)->remove(dir, dentry); error = NFS_PROTO(dir)->remove(dir, dentry);
if (error == -ENOENT) if (error == -ENOENT)
@ -2709,6 +2735,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
{ {
struct inode *old_inode = d_inode(old_dentry); struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry); struct inode *new_inode = d_inode(new_dentry);
unsigned long new_gencount = 0;
struct dentry *dentry = NULL; struct dentry *dentry = NULL;
struct rpc_task *task; struct rpc_task *task;
bool must_unblock = false; bool must_unblock = false;
@ -2761,6 +2788,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
} else { } else {
block_revalidate(new_dentry); block_revalidate(new_dentry);
must_unblock = true; must_unblock = true;
new_gencount = NFS_I(new_inode)->attr_gencount;
spin_unlock(&new_dentry->d_lock); spin_unlock(&new_dentry->d_lock);
} }
@ -2800,7 +2828,7 @@ out:
new_dir, new_dentry, error); new_dir, new_dentry, error);
if (!error) { if (!error) {
if (new_inode != NULL) 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 * 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 * handler because we need the proper locks to move the dentry. If

View File

@ -1389,6 +1389,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
status = pnfs_sync_inode(inode, false); status = pnfs_sync_inode(inode, false);
if (status) if (status)
goto out; goto out;
} else if (nfs_have_directory_delegation(inode)) {
status = 0;
goto out;
} }
status = -ENOMEM; status = -ENOMEM;

View File

@ -13,7 +13,7 @@
#include <linux/nfslocalio.h> #include <linux/nfslocalio.h>
#include <linux/wait_bit.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; extern const struct export_operations nfs_export_ops;
@ -152,7 +152,6 @@ struct nfs_fs_context {
struct super_block *sb; struct super_block *sb;
struct dentry *dentry; struct dentry *dentry;
struct nfs_fattr *fattr; struct nfs_fattr *fattr;
unsigned int inherited_bsize;
} clone_data; } clone_data;
}; };

View File

@ -43,8 +43,8 @@ struct nfs_local_kiocb {
size_t end_len; size_t end_len;
short int end_iter_index; short int end_iter_index;
atomic_t n_iters; atomic_t n_iters;
struct iov_iter iters[NFSLOCAL_MAX_IOS];
bool iter_is_dio_aligned[NFSLOCAL_MAX_IOS]; bool iter_is_dio_aligned[NFSLOCAL_MAX_IOS];
struct iov_iter iters[NFSLOCAL_MAX_IOS] ____cacheline_aligned;
/* End mostly DIO-specific members */ /* End mostly DIO-specific members */
}; };
@ -339,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)) if (unlikely(!nf_dio_mem_align || !nf_dio_offset_align))
return false; return false;
if (unlikely(nf_dio_offset_align > PAGE_SIZE))
return false;
if (unlikely(len < nf_dio_offset_align)) if (unlikely(len < nf_dio_offset_align))
return false; return false;

View File

@ -149,6 +149,7 @@ struct vfsmount *nfs_d_automount(struct path *path)
struct vfsmount *mnt = ERR_PTR(-ENOMEM); struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct nfs_server *server = NFS_SB(path->dentry->d_sb); struct nfs_server *server = NFS_SB(path->dentry->d_sb);
struct nfs_client *client = server->nfs_client; 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 timeout = READ_ONCE(nfs_mountpoint_expiry_timeout);
int ret; int ret;
@ -169,11 +170,21 @@ struct vfsmount *nfs_d_automount(struct path *path)
if (!ctx->clone_data.fattr) if (!ctx->clone_data.fattr)
goto out_fc; 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) { if (fc->net_ns != client->cl_net) {
put_net(fc->net_ns); put_net(fc->net_ns);
fc->net_ns = get_net(client->cl_net); 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 */ /* for submounts we want the same server; referrals will reassign */
memcpy(&ctx->nfs_server._address, &client->cl_addr, client->cl_addrlen); memcpy(&ctx->nfs_server._address, &client->cl_addr, client->cl_addrlen);
ctx->nfs_server.addrlen = 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; ctx->nfs_mod = client->cl_nfs_mod;
get_nfs_version(ctx->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); ret = client->rpc_ops->submount(fc, server);
if (ret < 0) { if (ret < 0) {
mnt = ERR_PTR(ret); mnt = ERR_PTR(ret);
@ -283,7 +298,6 @@ int nfs_do_submount(struct fs_context *fc)
return -ENOMEM; return -ENOMEM;
ctx->internal = true; ctx->internal = true;
ctx->clone_data.inherited_bsize = ctx->clone_data.sb->s_blocksize_bits;
p = nfs_devname(dentry, buffer, 4096); p = nfs_devname(dentry, buffer, 4096);
if (IS_ERR(p)) { if (IS_ERR(p)) {

View File

@ -483,7 +483,8 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void static void
nfs3_proc_rename_setup(struct rpc_message *msg, nfs3_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry, struct dentry *old_dentry,
struct dentry *new_dentry) struct dentry *new_dentry,
struct inode *same_parent)
{ {
msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME]; msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME];
} }

View File

@ -281,8 +281,13 @@ error:
*/ */
static void nfs4_destroy_callback(struct nfs_client *clp) static void nfs4_destroy_callback(struct nfs_client *clp)
{ {
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) {
nfs_callback_down(clp->cl_mvops->minor_version, clp->cl_net); 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) static void nfs4_shutdown_client(struct nfs_client *clp)
@ -1174,10 +1179,20 @@ static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
if (error < 0) if (error < 0)
return error; return error;
if (ctx->rsize) if (ctx->bsize) {
server->rsize = nfs_io_size(ctx->rsize, server->nfs_client->cl_proto); server->bsize = ctx->bsize;
if (ctx->wsize) server->automount_inherit |= NFS_AUTOMOUNT_INHERIT_BSIZE;
server->wsize = nfs_io_size(ctx->wsize, server->nfs_client->cl_proto); }
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->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ; server->acregmax = ctx->acregmax * HZ;

View File

@ -1780,8 +1780,17 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
if (nfs_stateid_is_sequential(state, stateid)) if (nfs_stateid_is_sequential(state, stateid))
break; break;
if (status) if (status) {
break; 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 */ /* Rely on seqids for serialisation with NFSv4.0 */
if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client)) if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
break; 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) if (opendata->o_res.rflags & NFS4_OPEN_RESULT_PRESERVE_UNLINKED)
set_bit(NFS_INO_PRESERVE_UNLINKED, &NFS_I(state->inode)->flags); 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) { switch(opendata->o_arg.claim) {
default: default:
break; break;
@ -3196,7 +3193,20 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
break; break;
if (opendata->o_res.delegation.type != 0) if (opendata->o_res.delegation.type != 0)
dir_verifier = nfs_save_change_attribute(dir); 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 */ /* Parse layoutget results before we check for access */
@ -4460,6 +4470,30 @@ out:
return status; 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, static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr, struct inode *inode) 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_argp = &args,
.rpc_resp = &res, .rpc_resp = &res,
}; };
struct nfs4_gdd_res gdd_res;
unsigned short task_flags = 0; unsigned short task_flags = 0;
int status;
if (nfs4_has_session(server->nfs_client)) if (nfs4_has_session(server->nfs_client))
task_flags = RPC_TASK_MOVEABLE; 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)) if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
task_flags |= RPC_TASK_TIMEOUT; 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); nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, fattr->label), inode, 0);
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); 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, 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 { do {
err = _nfs4_proc_getattr(server, fhandle, fattr, inode); err = _nfs4_proc_getattr(server, fhandle, fattr, inode);
trace_nfs4_getattr(server, fhandle, fattr, err); trace_nfs4_getattr(server, fhandle, fattr, err);
err = nfs4_handle_exception(server, err, switch (err) {
&exception); default:
err = nfs4_handle_exception(server, err, &exception);
break;
case -ENOTSUPP:
case -EOPNOTSUPP:
exception.retry = true;
}
} while (exception.retry); } while (exception.retry);
return err; return err;
} }
@ -4768,6 +4830,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
int status = 0; int status = 0;
if (!nfs4_have_delegation(inode, FMODE_READ, 0)) { if (!nfs4_have_delegation(inode, FMODE_READ, 0)) {
nfs_request_directory_delegation(inode);
res.fattr = nfs_alloc_fattr(); res.fattr = nfs_alloc_fattr();
if (res.fattr == NULL) if (res.fattr == NULL)
return -ENOMEM; return -ENOMEM;
@ -4875,6 +4938,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
ilabel = nfs4_label_init_security(dir, dentry, sattr, &l); ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
nfs_request_directory_delegation(dir);
if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK)) if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
sattr->ia_mode &= ~current_umask(); sattr->ia_mode &= ~current_umask();
state = nfs4_do_open(dir, ctx, flags, sattr, ilabel, NULL); state = nfs4_do_open(dir, ctx, flags, sattr, ilabel, NULL);
@ -4971,6 +5036,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg,
nfs4_init_sequence(&args->seq_args, &res->seq_res, 1, 0); nfs4_init_sequence(&args->seq_args, &res->seq_res, 1, 0);
nfs_fattr_init(res->dir_attr); nfs_fattr_init(res->dir_attr);
nfs_request_directory_delegation(d_inode(dentry->d_parent));
if (inode) { if (inode) {
nfs4_inode_return_delegation(inode); nfs4_inode_return_delegation(inode);
@ -5005,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, static void nfs4_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry, 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_renameargs *arg = msg->rpc_argp;
struct nfs_renameres *res = msg->rpc_resp; struct nfs_renameres *res = msg->rpc_resp;
@ -5016,6 +5083,8 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg,
nfs4_inode_make_writeable(old_inode); nfs4_inode_make_writeable(old_inode);
if (new_inode) if (new_inode)
nfs4_inode_return_delegation(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]; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
res->server = NFS_SB(old_dentry->d_sb); res->server = NFS_SB(old_dentry->d_sb);
nfs4_init_sequence(&arg->seq_args, &res->seq_res, 1, 0); nfs4_init_sequence(&arg->seq_args, &res->seq_res, 1, 0);
@ -10822,6 +10891,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
.minor_version = 1, .minor_version = 1,
.init_caps = NFS_CAP_READDIRPLUS .init_caps = NFS_CAP_READDIRPLUS
| NFS_CAP_ATOMIC_OPEN | NFS_CAP_ATOMIC_OPEN
| NFS_CAP_DIR_DELEG
| NFS_CAP_POSIX_LOCK | NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41 | NFS_CAP_STATEID_NFSV41
| NFS_CAP_ATOMIC_OPEN_V1 | NFS_CAP_ATOMIC_OPEN_V1
@ -10848,6 +10918,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
.minor_version = 2, .minor_version = 2,
.init_caps = NFS_CAP_READDIRPLUS .init_caps = NFS_CAP_READDIRPLUS
| NFS_CAP_ATOMIC_OPEN | NFS_CAP_ATOMIC_OPEN
| NFS_CAP_DIR_DELEG
| NFS_CAP_POSIX_LOCK | NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41 | NFS_CAP_STATEID_NFSV41
| NFS_CAP_ATOMIC_OPEN_V1 | NFS_CAP_ATOMIC_OPEN_V1

View File

@ -1353,6 +1353,7 @@ DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_setattr);
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_delegreturn); 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);
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update_wait); 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); DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_close_stateid_update_wait);
DECLARE_EVENT_CLASS(nfs4_getattr_event, DECLARE_EVENT_CLASS(nfs4_getattr_event,

View File

@ -393,6 +393,20 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5) XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5)
#define encode_reclaim_complete_maxsz (op_encode_hdr_maxsz + 4) #define encode_reclaim_complete_maxsz (op_encode_hdr_maxsz + 4)
#define decode_reclaim_complete_maxsz (op_decode_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 + \ #define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \ XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
1 /* layout type */ + \ 1 /* layout type */ + \
@ -444,6 +458,8 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#else /* CONFIG_NFS_V4_1 */ #else /* CONFIG_NFS_V4_1 */
#define encode_sequence_maxsz 0 #define encode_sequence_maxsz 0
#define decode_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 encode_layoutreturn_maxsz 0
#define decode_layoutreturn_maxsz 0 #define decode_layoutreturn_maxsz 0
#define encode_layoutget_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 + \ #define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \ encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_get_dir_deleg_maxsz + \
encode_getattr_maxsz + \ encode_getattr_maxsz + \
encode_renew_maxsz) encode_renew_maxsz)
#define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \ decode_sequence_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_get_dir_deleg_maxsz + \
decode_getattr_maxsz + \ decode_getattr_maxsz + \
decode_renew_maxsz) decode_renew_maxsz)
#define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \
@ -2007,6 +2025,33 @@ static void encode_sequence(struct xdr_stream *xdr,
} }
#ifdef CONFIG_NFS_V4_1 #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 static void
encode_getdeviceinfo(struct xdr_stream *xdr, encode_getdeviceinfo(struct xdr_stream *xdr,
const struct nfs4_getdeviceinfo_args *args, const struct nfs4_getdeviceinfo_args *args,
@ -2142,6 +2187,11 @@ static void encode_free_stateid(struct xdr_stream *xdr,
encode_nfs4_stateid(xdr, &args->stateid); encode_nfs4_stateid(xdr, &args->stateid);
} }
#else #else
static inline void
encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
}
static inline void static inline void
encode_layoutreturn(struct xdr_stream *xdr, encode_layoutreturn(struct xdr_stream *xdr,
const struct nfs4_layoutreturn_args *args, const struct nfs4_layoutreturn_args *args,
@ -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_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr); encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &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_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&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); 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, static int decode_getdeviceinfo(struct xdr_stream *xdr,
struct nfs4_getdeviceinfo_res *res) struct nfs4_getdeviceinfo_res *res)
{ {
@ -6208,6 +6303,12 @@ static int decode_free_stateid(struct xdr_stream *xdr,
return res->status; return res->status;
} }
#else #else
static int decode_get_dir_delegation(struct xdr_stream *xdr,
struct nfs4_getattr_res *res)
{
return 0;
}
static inline static inline
int decode_layoutreturn(struct xdr_stream *xdr, int decode_layoutreturn(struct xdr_stream *xdr,
struct nfs4_layoutreturn_res *res) 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); status = decode_putfh(xdr);
if (status) if (status)
goto out; 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); status = decode_getfattr(xdr, res->fattr, res->server);
out: out:
return status; return status;

View File

@ -464,6 +464,7 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
struct pnfs_layout_segment *lseg, *next; struct pnfs_layout_segment *lseg, *next;
set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags); 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) list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
pnfs_clear_lseg_state(lseg, lseg_list); pnfs_clear_lseg_state(lseg, lseg_list);
pnfs_clear_layoutreturn_info(lo); pnfs_clear_layoutreturn_info(lo);

View File

@ -353,7 +353,8 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void static void
nfs_proc_rename_setup(struct rpc_message *msg, nfs_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry, struct dentry *old_dentry,
struct dentry *new_dentry) struct dentry *new_dentry,
struct inode *same_parent)
{ {
msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME]; msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME];
} }

View File

@ -1051,16 +1051,6 @@ int nfs_reconfigure(struct fs_context *fc)
sync_filesystem(sb); 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 * Userspace mount programs that send binary options generally send
* them populated with default values. We have no way to know which * them populated with default values. We have no way to know which
@ -1101,8 +1091,9 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx)
sb->s_blocksize = 0; sb->s_blocksize = 0;
sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr; sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr;
sb->s_op = server->nfs_client->cl_nfs_mod->sops; sb->s_op = server->nfs_client->cl_nfs_mod->sops;
if (ctx->bsize) if (server->bsize)
sb->s_blocksize = nfs_block_size(ctx->bsize, &sb->s_blocksize_bits); sb->s_blocksize =
nfs_block_size(server->bsize, &sb->s_blocksize_bits);
switch (server->nfs_client->rpc_ops->version) { switch (server->nfs_client->rpc_ops->version) {
case 2: case 2:
@ -1318,26 +1309,13 @@ int nfs_get_tree_common(struct fs_context *fc)
if (IS_ERR(server)) if (IS_ERR(server))
return PTR_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) if (server->flags & NFS_MOUNT_UNSHARED)
compare_super = NULL; compare_super = NULL;
else
fc->sb_flags &= ~SB_RDONLY;
/* -o noac implies -o sync */ /* -o noac implies -o sync */
if (server->flags & NFS_MOUNT_NOAC) if (server->flags & NFS_MOUNT_NOAC)
fc->sb_flags |= SB_SYNCHRONOUS; 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 */ /* Get a superblock - note that we may end up sharing one that already exists */
fc->s_fs_info = server; fc->s_fs_info = server;
s = sget_fc(fc, compare_super, nfs_set_super); 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) { if (!s->s_root) {
unsigned bsize = ctx->clone_data.inherited_bsize;
/* initial superblock/root creation */ /* initial superblock/root creation */
nfs_fill_super(s, ctx); nfs_fill_super(s, ctx);
if (bsize) {
s->s_blocksize_bits = bsize;
s->s_blocksize = 1U << bsize;
}
error = nfs_get_cache_cookie(s, ctx); error = nfs_get_cache_cookie(s, ctx);
if (error < 0) if (error < 0)
goto error_splat_super; goto error_splat_super;

View File

@ -390,7 +390,8 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
nfs_sb_active(old_dir->i_sb); 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); return rpc_run_task(&task_setup_data);
} }

View File

@ -344,6 +344,7 @@ struct nfs4_copy_state {
#define NFS_INO_LAYOUTCOMMITTING (10) /* layoutcommit inflight */ #define NFS_INO_LAYOUTCOMMITTING (10) /* layoutcommit inflight */
#define NFS_INO_LAYOUTSTATS (11) /* layoutstats inflight */ #define NFS_INO_LAYOUTSTATS (11) /* layoutstats inflight */
#define NFS_INO_ODIRECT (12) /* I/O setting is O_DIRECT */ #define NFS_INO_ODIRECT (12) /* I/O setting is O_DIRECT */
#define NFS_INO_REQ_DIR_DELEG (13) /* Request a directory delegation */
static inline struct nfs_inode *NFS_I(const struct inode *inode) static inline struct nfs_inode *NFS_I(const struct inode *inode)
{ {

View File

@ -172,6 +172,11 @@ struct nfs_server {
#define NFS_MOUNT_FORCE_RDIRPLUS 0x20000000 #define NFS_MOUNT_FORCE_RDIRPLUS 0x20000000
#define NFS_MOUNT_NETUNREACH_FATAL 0x40000000 #define NFS_MOUNT_NETUNREACH_FATAL 0x40000000
unsigned int automount_inherit; /* Properties inherited by automount */
#define NFS_AUTOMOUNT_INHERIT_BSIZE 0x0001
#define NFS_AUTOMOUNT_INHERIT_RSIZE 0x0002
#define NFS_AUTOMOUNT_INHERIT_WSIZE 0x0004
unsigned int caps; /* server capabilities */ unsigned int caps; /* server capabilities */
__u64 fattr_valid; /* Valid attributes */ __u64 fattr_valid; /* Valid attributes */
unsigned int rsize; /* read size */ unsigned int rsize; /* read size */
@ -305,6 +310,7 @@ struct nfs_server {
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8) #define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
#define NFS_CAP_OFFLOAD_STATUS (1U << 9) #define NFS_CAP_OFFLOAD_STATUS (1U << 9)
#define NFS_CAP_ZERO_RANGE (1U << 10) #define NFS_CAP_ZERO_RANGE (1U << 10)
#define NFS_CAP_DIR_DELEG (1U << 11)
#define NFS_CAP_OPEN_XOR (1U << 12) #define NFS_CAP_OPEN_XOR (1U << 12)
#define NFS_CAP_DELEGTIME (1U << 13) #define NFS_CAP_DELEGTIME (1U << 13)
#define NFS_CAP_POSIX_LOCK (1U << 14) #define NFS_CAP_POSIX_LOCK (1U << 14)

View File

@ -1092,12 +1092,19 @@ struct nfs4_getattr_arg {
struct nfs4_sequence_args seq_args; struct nfs4_sequence_args seq_args;
const struct nfs_fh * fh; const struct nfs_fh * fh;
const u32 * bitmask; const u32 * bitmask;
bool get_dir_deleg;
};
struct nfs4_gdd_res {
u32 status;
nfs4_stateid deleg;
}; };
struct nfs4_getattr_res { struct nfs4_getattr_res {
struct nfs4_sequence_res seq_res; struct nfs4_sequence_res seq_res;
const struct nfs_server * server; const struct nfs_server * server;
struct nfs_fattr * fattr; struct nfs_fattr * fattr;
struct nfs4_gdd_res * gdd_res;
}; };
struct nfs4_link_arg { struct nfs4_link_arg {
@ -1801,7 +1808,8 @@ struct nfs_rpc_ops {
int (*unlink_done) (struct rpc_task *, struct inode *); int (*unlink_done) (struct rpc_task *, struct inode *);
void (*rename_setup) (struct rpc_message *msg, void (*rename_setup) (struct rpc_message *msg,
struct dentry *old_dentry, struct dentry *old_dentry,
struct dentry *new_dentry); struct dentry *new_dentry,
struct inode *same_parent);
void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *); void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *);
int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir); int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir);
int (*link) (struct inode *, struct inode *, const struct qstr *); int (*link) (struct inode *, struct inode *, const struct qstr *);

View File

@ -25,12 +25,14 @@ void xprt_init_bc_request(struct rpc_rqst *req, struct rpc_task *task,
void xprt_free_bc_request(struct rpc_rqst *req); void xprt_free_bc_request(struct rpc_rqst *req);
int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs); int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs);
void xprt_destroy_backchannel(struct rpc_xprt *, unsigned int max_reqs); void xprt_destroy_backchannel(struct rpc_xprt *, unsigned int max_reqs);
void xprt_enqueue_bc_request(struct rpc_rqst *req);
/* Socket backchannel transport methods */ /* Socket backchannel transport methods */
int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs); int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs);
void xprt_destroy_bc(struct rpc_xprt *xprt, unsigned int max_reqs); void xprt_destroy_bc(struct rpc_xprt *xprt, unsigned int max_reqs);
void xprt_free_bc_rqst(struct rpc_rqst *req); void xprt_free_bc_rqst(struct rpc_rqst *req);
unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt); unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt);
void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv);
/* /*
* Determine if a shared backchannel is in use * Determine if a shared backchannel is in use
@ -68,5 +70,10 @@ static inline void set_bc_enabled(struct svc_serv *serv)
static inline void xprt_free_bc_request(struct rpc_rqst *req) static inline void xprt_free_bc_request(struct rpc_rqst *req)
{ {
} }
static inline void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv)
{
svc_destroy(serv);
}
#endif /* CONFIG_SUNRPC_BACKCHANNEL */ #endif /* CONFIG_SUNRPC_BACKCHANNEL */
#endif /* _LINUX_SUNRPC_BC_XPRT_H */ #endif /* _LINUX_SUNRPC_BC_XPRT_H */

View File

@ -24,6 +24,22 @@ unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt)
return BC_MAX_SLOTS; return BC_MAX_SLOTS;
} }
/*
* Helper function to nullify backchannel server pointer in transport.
* We need to synchronize setting the pointer to NULL (done so after
* the backchannel server is shutdown) with the usage of that pointer
* by the backchannel request processing routines
* xprt_complete_bc_request() and rpcrdma_bc_receive_call().
*/
void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv)
{
spin_lock(&xprt->bc_pa_lock);
svc_destroy(serv);
xprt->bc_serv = NULL;
spin_unlock(&xprt->bc_pa_lock);
}
EXPORT_SYMBOL_GPL(xprt_svc_destroy_nullify_bc);
/* /*
* Helper routines that track the number of preallocation elements * Helper routines that track the number of preallocation elements
* on the transport. * on the transport.
@ -354,7 +370,6 @@ found:
void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied) void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied)
{ {
struct rpc_xprt *xprt = req->rq_xprt; struct rpc_xprt *xprt = req->rq_xprt;
struct svc_serv *bc_serv = xprt->bc_serv;
spin_lock(&xprt->bc_pa_lock); spin_lock(&xprt->bc_pa_lock);
list_del(&req->rq_bc_pa_list); list_del(&req->rq_bc_pa_list);
@ -365,7 +380,21 @@ void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied)
set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
dprintk("RPC: add callback request to list\n"); dprintk("RPC: add callback request to list\n");
xprt_get(xprt); xprt_enqueue_bc_request(req);
lwq_enqueue(&req->rq_bc_list, &bc_serv->sv_cb_list);
svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
} }
void xprt_enqueue_bc_request(struct rpc_rqst *req)
{
struct rpc_xprt *xprt = req->rq_xprt;
struct svc_serv *bc_serv;
xprt_get(xprt);
spin_lock(&xprt->bc_pa_lock);
bc_serv = xprt->bc_serv;
if (bc_serv) {
lwq_enqueue(&req->rq_bc_list, &bc_serv->sv_cb_list);
svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
}
spin_unlock(&xprt->bc_pa_lock);
}
EXPORT_SYMBOL_GPL(xprt_enqueue_bc_request);

View File

@ -9,6 +9,7 @@
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/svc_rdma.h> #include <linux/sunrpc/svc_rdma.h>
#include <linux/sunrpc/bc_xprt.h>
#include "xprt_rdma.h" #include "xprt_rdma.h"
#include <trace/events/rpcrdma.h> #include <trace/events/rpcrdma.h>
@ -220,7 +221,6 @@ void rpcrdma_bc_receive_call(struct rpcrdma_xprt *r_xprt,
struct rpcrdma_rep *rep) struct rpcrdma_rep *rep)
{ {
struct rpc_xprt *xprt = &r_xprt->rx_xprt; struct rpc_xprt *xprt = &r_xprt->rx_xprt;
struct svc_serv *bc_serv;
struct rpcrdma_req *req; struct rpcrdma_req *req;
struct rpc_rqst *rqst; struct rpc_rqst *rqst;
struct xdr_buf *buf; struct xdr_buf *buf;
@ -261,11 +261,7 @@ void rpcrdma_bc_receive_call(struct rpcrdma_xprt *r_xprt,
trace_xprtrdma_cb_call(r_xprt, rqst); trace_xprtrdma_cb_call(r_xprt, rqst);
/* Queue rqst for ULP's callback service */ /* Queue rqst for ULP's callback service */
bc_serv = xprt->bc_serv; xprt_enqueue_bc_request(rqst);
xprt_get(xprt);
lwq_enqueue(&rqst->rq_bc_list, &bc_serv->sv_cb_list);
svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
r_xprt->rx_stats.bcall_count++; r_xprt->rx_stats.bcall_count++;
return; return;