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
commit
6bb34aff1e
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
46
fs/nfs/dir.c
46
fs/nfs/dir.c
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)) {
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
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)
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 *);
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue