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
...
master
commit
6bb34aff1e
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -784,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;
|
||||
|
|
@ -977,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
46
fs/nfs/dir.c
46
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:
|
||||
|
|
@ -2202,6 +2216,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;
|
||||
|
|
@ -2507,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)
|
||||
|
|
@ -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 *new_inode = d_inode(new_dentry);
|
||||
unsigned long new_gencount = 0;
|
||||
struct dentry *dentry = NULL;
|
||||
struct rpc_task *task;
|
||||
bool must_unblock = false;
|
||||
|
|
@ -2761,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);
|
||||
}
|
||||
|
||||
|
|
@ -2800,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
|
||||
|
|
|
|||
|
|
@ -1389,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ struct nfs_local_kiocb {
|
|||
size_t end_len;
|
||||
short int end_iter_index;
|
||||
atomic_t n_iters;
|
||||
struct iov_iter iters[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 */
|
||||
};
|
||||
|
||||
|
|
@ -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))
|
||||
return false;
|
||||
if (unlikely(nf_dio_offset_align > PAGE_SIZE))
|
||||
return false;
|
||||
if (unlikely(len < nf_dio_offset_align))
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,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)
|
||||
|
|
@ -1174,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -4768,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;
|
||||
|
|
@ -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);
|
||||
|
||||
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);
|
||||
|
|
@ -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);
|
||||
|
||||
nfs_fattr_init(res->dir_attr);
|
||||
nfs_request_directory_delegation(d_inode(dentry->d_parent));
|
||||
|
||||
if (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,
|
||||
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;
|
||||
|
|
@ -5016,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);
|
||||
|
|
@ -10822,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
|
||||
|
|
@ -10848,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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
106
fs/nfs/nfs4xdr.c
106
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 + \
|
||||
|
|
@ -2007,6 +2025,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,
|
||||
|
|
@ -2142,6 +2187,11 @@ static void encode_free_stateid(struct xdr_stream *xdr,
|
|||
encode_nfs4_stateid(xdr, &args->stateid);
|
||||
}
|
||||
#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,
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1051,16 +1051,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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -344,6 +344,7 @@ struct nfs4_copy_state {
|
|||
#define NFS_INO_LAYOUTCOMMITTING (10) /* layoutcommit inflight */
|
||||
#define NFS_INO_LAYOUTSTATS (11) /* layoutstats inflight */
|
||||
#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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -172,6 +172,11 @@ struct nfs_server {
|
|||
#define NFS_MOUNT_FORCE_RDIRPLUS 0x20000000
|
||||
#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 */
|
||||
__u64 fattr_valid; /* Valid attributes */
|
||||
unsigned int rsize; /* read size */
|
||||
|
|
@ -305,6 +310,7 @@ struct nfs_server {
|
|||
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
|
||||
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
|
||||
#define NFS_CAP_ZERO_RANGE (1U << 10)
|
||||
#define NFS_CAP_DIR_DELEG (1U << 11)
|
||||
#define NFS_CAP_OPEN_XOR (1U << 12)
|
||||
#define NFS_CAP_DELEGTIME (1U << 13)
|
||||
#define NFS_CAP_POSIX_LOCK (1U << 14)
|
||||
|
|
|
|||
|
|
@ -1092,12 +1092,19 @@ struct nfs4_getattr_arg {
|
|||
struct nfs4_sequence_args seq_args;
|
||||
const struct nfs_fh * fh;
|
||||
const u32 * bitmask;
|
||||
bool get_dir_deleg;
|
||||
};
|
||||
|
||||
struct nfs4_gdd_res {
|
||||
u32 status;
|
||||
nfs4_stateid deleg;
|
||||
};
|
||||
|
||||
struct nfs4_getattr_res {
|
||||
struct nfs4_sequence_res seq_res;
|
||||
const struct nfs_server * server;
|
||||
struct nfs_fattr * fattr;
|
||||
struct nfs4_gdd_res * gdd_res;
|
||||
};
|
||||
|
||||
struct nfs4_link_arg {
|
||||
|
|
@ -1801,7 +1808,8 @@ struct nfs_rpc_ops {
|
|||
int (*unlink_done) (struct rpc_task *, struct inode *);
|
||||
void (*rename_setup) (struct rpc_message *msg,
|
||||
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 *);
|
||||
int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir);
|
||||
int (*link) (struct inode *, struct inode *, const struct qstr *);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_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 */
|
||||
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_free_bc_rqst(struct rpc_rqst *req);
|
||||
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
|
||||
|
|
@ -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_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv)
|
||||
{
|
||||
svc_destroy(serv);
|
||||
}
|
||||
#endif /* CONFIG_SUNRPC_BACKCHANNEL */
|
||||
#endif /* _LINUX_SUNRPC_BC_XPRT_H */
|
||||
|
|
|
|||
|
|
@ -24,6 +24,22 @@ unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt)
|
|||
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
|
||||
* on the transport.
|
||||
|
|
@ -354,7 +370,6 @@ found:
|
|||
void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied)
|
||||
{
|
||||
struct rpc_xprt *xprt = req->rq_xprt;
|
||||
struct svc_serv *bc_serv = xprt->bc_serv;
|
||||
|
||||
spin_lock(&xprt->bc_pa_lock);
|
||||
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);
|
||||
|
||||
dprintk("RPC: add callback request to list\n");
|
||||
xprt_get(xprt);
|
||||
lwq_enqueue(&req->rq_bc_list, &bc_serv->sv_cb_list);
|
||||
svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
|
||||
xprt_enqueue_bc_request(req);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/svc_xprt.h>
|
||||
#include <linux/sunrpc/svc_rdma.h>
|
||||
#include <linux/sunrpc/bc_xprt.h>
|
||||
|
||||
#include "xprt_rdma.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 rpc_xprt *xprt = &r_xprt->rx_xprt;
|
||||
struct svc_serv *bc_serv;
|
||||
struct rpcrdma_req *req;
|
||||
struct rpc_rqst *rqst;
|
||||
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);
|
||||
|
||||
/* Queue rqst for ULP's callback service */
|
||||
bc_serv = xprt->bc_serv;
|
||||
xprt_get(xprt);
|
||||
lwq_enqueue(&rqst->rq_bc_list, &bc_serv->sv_cb_list);
|
||||
|
||||
svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
|
||||
xprt_enqueue_bc_request(rqst);
|
||||
|
||||
r_xprt->rx_stats.bcall_count++;
|
||||
return;
|
||||
|
|
|
|||
Loading…
Reference in New Issue