NFS Clent Updates for Linux 6.16
New Features:
* Implement the Sunrpc rfc2203 rpcsec_gss sequence number cache
* Add support for FALLOC_FL_ZERO_RANGE on NFS v4.2
* Add a localio sysfs attribute
Stable Fixes:
* Fix double-unlock bug in nfs_return_empty_folio()
* Don't check for OPEN feature support in v4.1
* Always probe for LOCALIO support asynchronously
* Prevent hang on NFS mounts with xprtsec=[m]tls
Other Bugfixes:
* xattr handlers should check for absent nfs filehandles
* Fix setattr caching of TIME_[MODIFY|ACCESS]_SET when timestamps are delegated
* Fix listxattr to return selinux security labels
* Connect to NFSv3 DS using TLS if MDS connection uses TLS
* Clear SB_RDONLY before getting a superblock, and ignore when remounting
* Fix incorrect handling of NFS error codes in nfs4_do_mkdir()
* Various nfs_localio fixes from Neil Brown that include fixing an
rcu compilation error found by older gcc versions.
* Update stats on flexfiles pNFS DSes when receiving NFS4ERR_DELAY
Cleanups:
* Add a refcount tracker for struct net in the nfs_client
* Allow FREE_STATEID to clean up delegations
* Always set NLINK even if the server doesn't support it
* Cleanups to the NFS folio writeback code
* Remove dead code from xs_tcp_tls_setup_socket()
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmg/YkAACgkQ18tUv7Cl
QOuGpQ/+OuG/xkVX6j7FerUcdbVhcZ+5jDUKC0cNe6EeFeFRjgqsdFB0uqH+AgJh
DlxEJuXTMq+9mcptl0rjrOn0tj7dlTpgZowp3kWdK3bX1zSI2jBEJjnz3xVzjBQx
3lbmF/UAIaHv5bPVc9aF8mioaj5DSRKWTBLTg7iOM1ol1DqgHK/M0q2D7d2n1yB4
WYGI7LlAWSBGV4PvEkhHW6PwVPDSqECPBvIxd1obq8TSNl+YZlmVxCoJ99+zVqWf
dvaDOwfs5x+YEQH/+N/XWdc38QiCGfu7H79qGHShWB8t/KT4axxmjVs2fT7xtUsv
yN3fb77rlFOCJaPLRF549/4EJqHYMWmFDKIMUZ7YC1vEBCG4B1kQUqarA5eCbsAi
s/rxBs1VNKeev/RecDaViAeH3XZoVU1rNyIBJjOuWgNlC5wnbF+An3zE0m8MAXxO
Vh7wQSH3GZEY+VCR6ljwLhIv6+tvSVQxEZKUUjfVQXp5UuNwN3wKa+sW6li+FBl6
uV6lJcmdUffrurNhvSghIiSQGDkerHUVhSltgtj5FnmRp/AM95Z850t5a7qqc7Cv
duks9siLLaeC4K5W+AOcKLWXho1dJMIPWUej3ErCiHWnA20QiNXsQN4QoimkDKqf
9SYdcl6UECqV5MzIa/L7cW96S3K0acrq+8ofJCjN3A8M0pcTGgU=
=5DFQ
-----END PGP SIGNATURE-----
Merge tag 'nfs-for-6.16-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
Pull NFS clent updates from Anna Schumaker:
"New Features:
- Implement the Sunrpc rfc2203 rpcsec_gss sequence number cache
- Add support for FALLOC_FL_ZERO_RANGE on NFS v4.2
- Add a localio sysfs attribute
Stable Fixes:
- Fix double-unlock bug in nfs_return_empty_folio()
- Don't check for OPEN feature support in v4.1
- Always probe for LOCALIO support asynchronously
- Prevent hang on NFS mounts with xprtsec=[m]tls
Other Bugfixes:
- xattr handlers should check for absent nfs filehandles
- Fix setattr caching of TIME_[MODIFY|ACCESS]_SET when timestamps are
delegated
- Fix listxattr to return selinux security labels
- Connect to NFSv3 DS using TLS if MDS connection uses TLS
- Clear SB_RDONLY before getting a superblock, and ignore when
remounting
- Fix incorrect handling of NFS error codes in nfs4_do_mkdir()
- Various nfs_localio fixes from Neil Brown that include fixing an
rcu compilation error found by older gcc versions.
- Update stats on flexfiles pNFS DSes when receiving NFS4ERR_DELAY
Cleanups:
- Add a refcount tracker for struct net in the nfs_client
- Allow FREE_STATEID to clean up delegations
- Always set NLINK even if the server doesn't support it
- Cleanups to the NFS folio writeback code
- Remove dead code from xs_tcp_tls_setup_socket()"
* tag 'nfs-for-6.16-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (30 commits)
flexfiles/pNFS: update stats on NFS4ERR_DELAY for v4.1 DSes
nfs_localio: change nfsd_file_put_local() to take a pointer to __rcu pointer
nfs_localio: protect race between nfs_uuid_put() and nfs_close_local_fh()
nfs_localio: duplicate nfs_close_local_fh()
nfs_localio: simplify interface to nfsd for getting nfsd_file
nfs_localio: always hold nfsd net ref with nfsd_file ref
nfs_localio: use cmpxchg() to install new nfs_file_localio
SUNRPC: Remove dead code from xs_tcp_tls_setup_socket()
SUNRPC: Prevent hang on NFS mount with xprtsec=[m]tls
nfs: fix incorrect handling of large-number NFS errors in nfs4_do_mkdir()
nfs: ignore SB_RDONLY when remounting nfs
nfs: clear SB_RDONLY before getting superblock
NFS: always probe for LOCALIO support asynchronously
pnfs/flexfiles: connect to NFSv3 DS using TLS if MDS connection uses TLS
NFS: add localio to sysfs
nfs: use writeback_iter directly
nfs: refactor nfs_do_writepage
nfs: don't return AOP_WRITEPAGE_ACTIVATE from nfs_do_writepage
nfs: fold nfs_page_async_flush into nfs_do_writepage
NFSv4: Always set NLINK even if the server doesn't support it
...
pull/1285/head
commit
5abc7438f1
|
|
@ -180,7 +180,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
|
|||
clp->cl_proto = cl_init->proto;
|
||||
clp->cl_nconnect = cl_init->nconnect;
|
||||
clp->cl_max_connect = cl_init->max_connect ? cl_init->max_connect : 1;
|
||||
clp->cl_net = get_net(cl_init->net);
|
||||
clp->cl_net = get_net_track(cl_init->net, &clp->cl_ns_tracker, GFP_KERNEL);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
seqlock_init(&clp->cl_boot_lock);
|
||||
|
|
@ -250,7 +250,7 @@ void nfs_free_client(struct nfs_client *clp)
|
|||
if (!IS_ERR(clp->cl_rpcclient))
|
||||
rpc_shutdown_client(clp->cl_rpcclient);
|
||||
|
||||
put_net(clp->cl_net);
|
||||
put_net_track(clp->cl_net, &clp->cl_ns_tracker);
|
||||
put_nfs_version(clp->cl_nfs_mod);
|
||||
kfree(clp->cl_hostname);
|
||||
kfree(clp->cl_acceptor);
|
||||
|
|
@ -439,7 +439,7 @@ struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
|
|||
spin_unlock(&nn->nfs_client_lock);
|
||||
new = rpc_ops->init_client(new, cl_init);
|
||||
if (!IS_ERR(new))
|
||||
nfs_local_probe(new);
|
||||
nfs_local_probe_async(new);
|
||||
return new;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1021,13 +1021,6 @@ out:
|
|||
nfs_inode_find_state_and_recover(inode, stateid);
|
||||
}
|
||||
|
||||
void nfs_remove_bad_delegation(struct inode *inode,
|
||||
const nfs4_stateid *stateid)
|
||||
{
|
||||
nfs_revoke_delegation(inode, stateid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
|
||||
|
||||
void nfs_delegation_mark_returned(struct inode *inode,
|
||||
const nfs4_stateid *stateid)
|
||||
{
|
||||
|
|
@ -1069,6 +1062,24 @@ out_rcu_unlock:
|
|||
nfs_inode_find_state_and_recover(inode, stateid);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_remove_bad_delegation - handle delegations that are unusable
|
||||
* @inode: inode to process
|
||||
* @stateid: the delegation's stateid
|
||||
*
|
||||
* If the server ACK-ed our FREE_STATEID then clean
|
||||
* up the delegation, else mark and keep the revoked state.
|
||||
*/
|
||||
void nfs_remove_bad_delegation(struct inode *inode,
|
||||
const nfs4_stateid *stateid)
|
||||
{
|
||||
if (stateid && stateid->type == NFS4_FREED_STATEID_TYPE)
|
||||
nfs_delegation_mark_returned(inode, stateid);
|
||||
else
|
||||
nfs_revoke_delegation(inode, stateid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
|
||||
|
||||
/**
|
||||
* nfs_expire_unused_delegation_types
|
||||
* @clp: client to process
|
||||
|
|
|
|||
|
|
@ -1129,6 +1129,8 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
|
|||
nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
|
||||
break;
|
||||
case -NFS4ERR_DELAY:
|
||||
nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY);
|
||||
fallthrough;
|
||||
case -NFS4ERR_GRACE:
|
||||
rpc_delay(task, FF_LAYOUT_POLL_RETRY_MAX);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
|
|||
* keep ds_clp even if DS is local, so that if local IO cannot
|
||||
* proceed somehow, we can fall back to NFS whenever we want.
|
||||
*/
|
||||
nfs_local_probe(ds->ds_clp);
|
||||
nfs_local_probe_async(ds->ds_clp);
|
||||
max_payload =
|
||||
nfs_block_size(rpc_max_payload(ds->ds_clp->cl_rpcclient),
|
||||
NULL);
|
||||
|
|
|
|||
|
|
@ -557,6 +557,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
|||
set_nlink(inode, fattr->nlink);
|
||||
else if (fattr_supported & NFS_ATTR_FATTR_NLINK)
|
||||
nfs_set_cache_invalid(inode, NFS_INO_INVALID_NLINK);
|
||||
else
|
||||
set_nlink(inode, 1);
|
||||
if (fattr->valid & NFS_ATTR_FATTR_OWNER)
|
||||
inode->i_uid = fattr->uid;
|
||||
else if (fattr_supported & NFS_ATTR_FATTR_OWNER)
|
||||
|
|
@ -633,6 +635,34 @@ nfs_fattr_fixup_delegated(struct inode *inode, struct nfs_fattr *fattr)
|
|||
}
|
||||
}
|
||||
|
||||
static void nfs_set_timestamps_to_ts(struct inode *inode, struct iattr *attr)
|
||||
{
|
||||
unsigned int cache_flags = 0;
|
||||
|
||||
if (attr->ia_valid & ATTR_MTIME_SET) {
|
||||
struct timespec64 ctime = inode_get_ctime(inode);
|
||||
struct timespec64 mtime = inode_get_mtime(inode);
|
||||
struct timespec64 now;
|
||||
int updated = 0;
|
||||
|
||||
now = inode_set_ctime_current(inode);
|
||||
if (!timespec64_equal(&now, &ctime))
|
||||
updated |= S_CTIME;
|
||||
|
||||
inode_set_mtime_to_ts(inode, attr->ia_mtime);
|
||||
if (!timespec64_equal(&now, &mtime))
|
||||
updated |= S_MTIME;
|
||||
|
||||
inode_maybe_inc_iversion(inode, updated);
|
||||
cache_flags |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
|
||||
}
|
||||
if (attr->ia_valid & ATTR_ATIME_SET) {
|
||||
inode_set_atime_to_ts(inode, attr->ia_atime);
|
||||
cache_flags |= NFS_INO_INVALID_ATIME;
|
||||
}
|
||||
NFS_I(inode)->cache_validity &= ~cache_flags;
|
||||
}
|
||||
|
||||
static void nfs_update_timestamps(struct inode *inode, unsigned int ia_valid)
|
||||
{
|
||||
enum file_time_flags time_flags = 0;
|
||||
|
|
@ -701,15 +731,28 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
|
||||
if (nfs_have_delegated_mtime(inode) && attr->ia_valid & ATTR_MTIME) {
|
||||
spin_lock(&inode->i_lock);
|
||||
if (attr->ia_valid & ATTR_MTIME_SET) {
|
||||
nfs_set_timestamps_to_ts(inode, attr);
|
||||
attr->ia_valid &= ~(ATTR_MTIME|ATTR_MTIME_SET|
|
||||
ATTR_ATIME|ATTR_ATIME_SET);
|
||||
} else {
|
||||
nfs_update_timestamps(inode, attr->ia_valid);
|
||||
attr->ia_valid &= ~(ATTR_MTIME|ATTR_ATIME);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
attr->ia_valid &= ~(ATTR_MTIME | ATTR_ATIME);
|
||||
} else if (nfs_have_delegated_atime(inode) &&
|
||||
attr->ia_valid & ATTR_ATIME &&
|
||||
!(attr->ia_valid & ATTR_MTIME)) {
|
||||
if (attr->ia_valid & ATTR_ATIME_SET) {
|
||||
spin_lock(&inode->i_lock);
|
||||
nfs_set_timestamps_to_ts(inode, attr);
|
||||
spin_unlock(&inode->i_lock);
|
||||
attr->ia_valid &= ~(ATTR_ATIME|ATTR_ATIME_SET);
|
||||
} else {
|
||||
nfs_update_delegated_atime(inode);
|
||||
attr->ia_valid &= ~ATTR_ATIME;
|
||||
}
|
||||
}
|
||||
|
||||
/* Optimization: if the end result is no change, don't RPC */
|
||||
if (((attr->ia_valid & NFS_VALID_ATTRS) & ~(ATTR_FILE|ATTR_OPEN)) == 0)
|
||||
|
|
|
|||
|
|
@ -455,7 +455,6 @@ extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
|
|||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
/* localio.c */
|
||||
extern void nfs_local_probe(struct nfs_client *);
|
||||
extern void nfs_local_probe_async(struct nfs_client *);
|
||||
extern void nfs_local_probe_async_work(struct work_struct *);
|
||||
extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ static bool nfs_server_uuid_is_local(struct nfs_client *clp)
|
|||
* - called after alloc_client and init_client (so cl_rpcclient exists)
|
||||
* - this function is idempotent, it can be called for old or new clients
|
||||
*/
|
||||
void nfs_local_probe(struct nfs_client *clp)
|
||||
static void nfs_local_probe(struct nfs_client *clp)
|
||||
{
|
||||
/* Disallow localio if disabled via sysfs or AUTH_SYS isn't used */
|
||||
if (!localio_enabled ||
|
||||
|
|
@ -191,14 +191,16 @@ void nfs_local_probe(struct nfs_client *clp)
|
|||
nfs_localio_enable_client(clp);
|
||||
nfs_uuid_end(&clp->cl_uuid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_local_probe);
|
||||
|
||||
void nfs_local_probe_async_work(struct work_struct *work)
|
||||
{
|
||||
struct nfs_client *clp =
|
||||
container_of(work, struct nfs_client, cl_local_probe_work);
|
||||
|
||||
if (!refcount_inc_not_zero(&clp->cl_count))
|
||||
return;
|
||||
nfs_local_probe(clp);
|
||||
nfs_put_client(clp);
|
||||
}
|
||||
|
||||
void nfs_local_probe_async(struct nfs_client *clp)
|
||||
|
|
@ -207,14 +209,16 @@ void nfs_local_probe_async(struct nfs_client *clp)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_local_probe_async);
|
||||
|
||||
static inline struct nfsd_file *nfs_local_file_get(struct nfsd_file *nf)
|
||||
static inline void nfs_local_file_put(struct nfsd_file *localio)
|
||||
{
|
||||
return nfs_to->nfsd_file_get(nf);
|
||||
}
|
||||
/* nfs_to_nfsd_file_put_local() expects an __rcu pointer
|
||||
* but we have a __kernel pointer. It is always safe
|
||||
* to cast a __kernel pointer to an __rcu pointer
|
||||
* because the cast only weakens what is known about the pointer.
|
||||
*/
|
||||
struct nfsd_file __rcu *nf = (struct nfsd_file __rcu*) localio;
|
||||
|
||||
static inline void nfs_local_file_put(struct nfsd_file *nf)
|
||||
{
|
||||
nfs_to->nfsd_file_put(nf);
|
||||
nfs_to_nfsd_file_put_local(&nf);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -226,12 +230,13 @@ static inline void nfs_local_file_put(struct nfsd_file *nf)
|
|||
static struct nfsd_file *
|
||||
__nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
||||
struct nfs_fh *fh, struct nfs_file_localio *nfl,
|
||||
struct nfsd_file __rcu **pnf,
|
||||
const fmode_t mode)
|
||||
{
|
||||
struct nfsd_file *localio;
|
||||
|
||||
localio = nfs_open_local_fh(&clp->cl_uuid, clp->cl_rpcclient,
|
||||
cred, fh, nfl, mode);
|
||||
cred, fh, nfl, pnf, mode);
|
||||
if (IS_ERR(localio)) {
|
||||
int status = PTR_ERR(localio);
|
||||
trace_nfs_local_open_fh(fh, mode, status);
|
||||
|
|
@ -258,7 +263,7 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
|||
struct nfs_fh *fh, struct nfs_file_localio *nfl,
|
||||
const fmode_t mode)
|
||||
{
|
||||
struct nfsd_file *nf, *new, __rcu **pnf;
|
||||
struct nfsd_file *nf, __rcu **pnf;
|
||||
|
||||
if (!nfs_server_is_local(clp))
|
||||
return NULL;
|
||||
|
|
@ -270,29 +275,9 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
|||
else
|
||||
pnf = &nfl->ro_file;
|
||||
|
||||
new = NULL;
|
||||
rcu_read_lock();
|
||||
nf = rcu_dereference(*pnf);
|
||||
if (!nf) {
|
||||
rcu_read_unlock();
|
||||
new = __nfs_local_open_fh(clp, cred, fh, nfl, mode);
|
||||
if (IS_ERR(new))
|
||||
nf = __nfs_local_open_fh(clp, cred, fh, nfl, pnf, mode);
|
||||
if (IS_ERR(nf))
|
||||
return NULL;
|
||||
rcu_read_lock();
|
||||
/* try to swap in the pointer */
|
||||
spin_lock(&clp->cl_uuid.lock);
|
||||
nf = rcu_dereference_protected(*pnf, 1);
|
||||
if (!nf) {
|
||||
nf = new;
|
||||
new = NULL;
|
||||
rcu_assign_pointer(*pnf, nf);
|
||||
}
|
||||
spin_unlock(&clp->cl_uuid.lock);
|
||||
}
|
||||
nf = nfs_local_file_get(nf);
|
||||
rcu_read_unlock();
|
||||
if (new)
|
||||
nfs_to_nfsd_file_put_local(new);
|
||||
return nf;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_local_open_fh);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ int nfs42_proc_allocate(struct file *, loff_t, loff_t);
|
|||
ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *, loff_t, size_t,
|
||||
struct nl4_server *, nfs4_stateid *, bool);
|
||||
int nfs42_proc_deallocate(struct file *, loff_t, loff_t);
|
||||
int nfs42_proc_zero_range(struct file *, loff_t, loff_t);
|
||||
loff_t nfs42_proc_llseek(struct file *, loff_t, int);
|
||||
int nfs42_proc_layoutstats_generic(struct nfs_server *,
|
||||
struct nfs42_layoutstat_data *);
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
|
|||
|
||||
err = nfs42_proc_fallocate(&msg, filep, offset, len);
|
||||
if (err == -EOPNOTSUPP)
|
||||
NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
|
||||
NFS_SERVER(inode)->caps &= ~(NFS_CAP_ALLOCATE |
|
||||
NFS_CAP_ZERO_RANGE);
|
||||
|
||||
inode_unlock(inode);
|
||||
return err;
|
||||
|
|
@ -169,7 +170,31 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
|
|||
if (err == 0)
|
||||
truncate_pagecache_range(inode, offset, (offset + len) -1);
|
||||
if (err == -EOPNOTSUPP)
|
||||
NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
|
||||
NFS_SERVER(inode)->caps &= ~(NFS_CAP_DEALLOCATE |
|
||||
NFS_CAP_ZERO_RANGE);
|
||||
|
||||
inode_unlock(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
int nfs42_proc_zero_range(struct file *filep, loff_t offset, loff_t len)
|
||||
{
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ZERO_RANGE],
|
||||
};
|
||||
struct inode *inode = file_inode(filep);
|
||||
int err;
|
||||
|
||||
if (!nfs_server_capable(inode, NFS_CAP_ZERO_RANGE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
err = nfs42_proc_fallocate(&msg, filep, offset, len);
|
||||
if (err == 0)
|
||||
truncate_pagecache_range(inode, offset, (offset + len) -1);
|
||||
if (err == -EOPNOTSUPP)
|
||||
NFS_SERVER(inode)->caps &= ~NFS_CAP_ZERO_RANGE;
|
||||
|
||||
inode_unlock(inode);
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -174,6 +174,18 @@
|
|||
decode_putfh_maxsz + \
|
||||
decode_deallocate_maxsz + \
|
||||
decode_getattr_maxsz)
|
||||
#define NFS4_enc_zero_range_sz (compound_encode_hdr_maxsz + \
|
||||
encode_sequence_maxsz + \
|
||||
encode_putfh_maxsz + \
|
||||
encode_deallocate_maxsz + \
|
||||
encode_allocate_maxsz + \
|
||||
encode_getattr_maxsz)
|
||||
#define NFS4_dec_zero_range_sz (compound_decode_hdr_maxsz + \
|
||||
decode_sequence_maxsz + \
|
||||
decode_putfh_maxsz + \
|
||||
decode_deallocate_maxsz + \
|
||||
decode_allocate_maxsz + \
|
||||
decode_getattr_maxsz)
|
||||
#define NFS4_enc_read_plus_sz (compound_encode_hdr_maxsz + \
|
||||
encode_sequence_maxsz + \
|
||||
encode_putfh_maxsz + \
|
||||
|
|
@ -648,6 +660,27 @@ static void nfs4_xdr_enc_deallocate(struct rpc_rqst *req,
|
|||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode ZERO_RANGE request
|
||||
*/
|
||||
static void nfs4_xdr_enc_zero_range(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
const void *data)
|
||||
{
|
||||
const struct nfs42_falloc_args *args = data;
|
||||
struct compound_hdr hdr = {
|
||||
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
|
||||
};
|
||||
|
||||
encode_compound_hdr(xdr, req, &hdr);
|
||||
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||
encode_putfh(xdr, args->falloc_fh, &hdr);
|
||||
encode_deallocate(xdr, args, &hdr);
|
||||
encode_allocate(xdr, args, &hdr);
|
||||
encode_getfattr(xdr, args->falloc_bitmask, &hdr);
|
||||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode READ_PLUS request
|
||||
*/
|
||||
|
|
@ -1510,6 +1543,37 @@ out:
|
|||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode ZERO_RANGE request
|
||||
*/
|
||||
static int nfs4_xdr_dec_zero_range(struct rpc_rqst *rqstp,
|
||||
struct xdr_stream *xdr,
|
||||
void *data)
|
||||
{
|
||||
struct nfs42_falloc_res *res = data;
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_sequence(xdr, &res->seq_res, rqstp);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_putfh(xdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_deallocate(xdr, res);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_allocate(xdr, res);
|
||||
if (status)
|
||||
goto out;
|
||||
decode_getfattr(xdr, res->falloc_fattr, res->falloc_server);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode READ_PLUS request
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -67,8 +67,7 @@ struct nfs4_minor_version_ops {
|
|||
void (*free_lock_state)(struct nfs_server *,
|
||||
struct nfs4_lock_state *);
|
||||
int (*test_and_free_expired)(struct nfs_server *,
|
||||
const nfs4_stateid *,
|
||||
const struct cred *);
|
||||
nfs4_stateid *, const struct cred *);
|
||||
struct nfs_seqid *
|
||||
(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
|
||||
void (*session_trunk)(struct rpc_clnt *clnt,
|
||||
|
|
|
|||
|
|
@ -225,8 +225,14 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
|
|||
if (!S_ISREG(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((mode != 0) && (mode != (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)))
|
||||
switch (mode) {
|
||||
case 0:
|
||||
case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
|
||||
case FALLOC_FL_ZERO_RANGE:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ret = inode_newsize_ok(inode, offset + len);
|
||||
if (ret < 0)
|
||||
|
|
@ -234,6 +240,8 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
|
|||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
||||
return nfs42_proc_deallocate(filep, offset, len);
|
||||
else if (mode & FALLOC_FL_ZERO_RANGE)
|
||||
return nfs42_proc_zero_range(filep, offset ,len);
|
||||
return nfs42_proc_allocate(filep, offset, len);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
|
|||
bool is_privileged);
|
||||
static int nfs41_test_stateid(struct nfs_server *, const nfs4_stateid *,
|
||||
const struct cred *);
|
||||
static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
|
||||
static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
|
||||
const struct cred *, bool);
|
||||
#endif
|
||||
|
||||
|
|
@ -325,14 +325,14 @@ static void nfs4_bitmap_copy_adjust(__u32 *dst, const __u32 *src,
|
|||
|
||||
if (nfs_have_delegated_mtime(inode)) {
|
||||
if (!(cache_validity & NFS_INO_INVALID_ATIME))
|
||||
dst[1] &= ~FATTR4_WORD1_TIME_ACCESS;
|
||||
dst[1] &= ~(FATTR4_WORD1_TIME_ACCESS|FATTR4_WORD1_TIME_ACCESS_SET);
|
||||
if (!(cache_validity & NFS_INO_INVALID_MTIME))
|
||||
dst[1] &= ~FATTR4_WORD1_TIME_MODIFY;
|
||||
dst[1] &= ~(FATTR4_WORD1_TIME_MODIFY|FATTR4_WORD1_TIME_MODIFY_SET);
|
||||
if (!(cache_validity & NFS_INO_INVALID_CTIME))
|
||||
dst[1] &= ~FATTR4_WORD1_TIME_METADATA;
|
||||
dst[1] &= ~(FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY_SET);
|
||||
} else if (nfs_have_delegated_atime(inode)) {
|
||||
if (!(cache_validity & NFS_INO_INVALID_ATIME))
|
||||
dst[1] &= ~FATTR4_WORD1_TIME_ACCESS;
|
||||
dst[1] &= ~(FATTR4_WORD1_TIME_ACCESS|FATTR4_WORD1_TIME_ACCESS_SET);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2903,16 +2903,14 @@ static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
|
|||
}
|
||||
|
||||
static int nfs40_test_and_free_expired_stateid(struct nfs_server *server,
|
||||
const nfs4_stateid *stateid,
|
||||
const struct cred *cred)
|
||||
nfs4_stateid *stateid, const struct cred *cred)
|
||||
{
|
||||
return -NFS4ERR_BAD_STATEID;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
static int nfs41_test_and_free_expired_stateid(struct nfs_server *server,
|
||||
const nfs4_stateid *stateid,
|
||||
const struct cred *cred)
|
||||
nfs4_stateid *stateid, const struct cred *cred)
|
||||
{
|
||||
int status;
|
||||
|
||||
|
|
@ -2921,6 +2919,7 @@ static int nfs41_test_and_free_expired_stateid(struct nfs_server *server,
|
|||
break;
|
||||
case NFS4_INVALID_STATEID_TYPE:
|
||||
case NFS4_SPECIAL_STATEID_TYPE:
|
||||
case NFS4_FREED_STATEID_TYPE:
|
||||
return -NFS4ERR_BAD_STATEID;
|
||||
case NFS4_REVOKED_STATEID_TYPE:
|
||||
goto out_free;
|
||||
|
|
@ -3976,8 +3975,9 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
|||
FATTR4_WORD0_CASE_INSENSITIVE |
|
||||
FATTR4_WORD0_CASE_PRESERVING;
|
||||
if (minorversion)
|
||||
bitmask[2] = FATTR4_WORD2_SUPPATTR_EXCLCREAT |
|
||||
FATTR4_WORD2_OPEN_ARGUMENTS;
|
||||
bitmask[2] = FATTR4_WORD2_SUPPATTR_EXCLCREAT;
|
||||
if (minorversion > 1)
|
||||
bitmask[2] |= FATTR4_WORD2_OPEN_ARGUMENTS;
|
||||
|
||||
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
if (status == 0) {
|
||||
|
|
@ -5164,13 +5164,15 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_
|
|||
}
|
||||
|
||||
static struct dentry *nfs4_do_mkdir(struct inode *dir, struct dentry *dentry,
|
||||
struct nfs4_createdata *data)
|
||||
struct nfs4_createdata *data, int *statusp)
|
||||
{
|
||||
int status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg,
|
||||
struct dentry *ret;
|
||||
|
||||
*statusp = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg,
|
||||
&data->arg.seq_args, &data->res.seq_res, 1);
|
||||
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
if (*statusp)
|
||||
return NULL;
|
||||
|
||||
spin_lock(&dir->i_lock);
|
||||
/* Creating a directory bumps nlink in the parent */
|
||||
|
|
@ -5179,7 +5181,11 @@ static struct dentry *nfs4_do_mkdir(struct inode *dir, struct dentry *dentry,
|
|||
data->res.fattr->time_start,
|
||||
NFS_INO_INVALID_DATA);
|
||||
spin_unlock(&dir->i_lock);
|
||||
return nfs_add_or_obtain(dentry, data->res.fh, data->res.fattr);
|
||||
ret = nfs_add_or_obtain(dentry, data->res.fh, data->res.fattr);
|
||||
if (!IS_ERR(ret))
|
||||
return ret;
|
||||
*statusp = PTR_ERR(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void nfs4_free_createdata(struct nfs4_createdata *data)
|
||||
|
|
@ -5240,17 +5246,18 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
static struct dentry *_nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
|
||||
struct iattr *sattr,
|
||||
struct nfs4_label *label)
|
||||
struct nfs4_label *label, int *statusp)
|
||||
{
|
||||
struct nfs4_createdata *data;
|
||||
struct dentry *ret = ERR_PTR(-ENOMEM);
|
||||
struct dentry *ret = NULL;
|
||||
|
||||
*statusp = -ENOMEM;
|
||||
data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4DIR);
|
||||
if (data == NULL)
|
||||
goto out;
|
||||
|
||||
data->arg.label = label;
|
||||
ret = nfs4_do_mkdir(dir, dentry, data);
|
||||
ret = nfs4_do_mkdir(dir, dentry, data, statusp);
|
||||
|
||||
nfs4_free_createdata(data);
|
||||
out:
|
||||
|
|
@ -5273,11 +5280,12 @@ static struct dentry *nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
|
|||
if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
|
||||
sattr->ia_mode &= ~current_umask();
|
||||
do {
|
||||
alias = _nfs4_proc_mkdir(dir, dentry, sattr, label);
|
||||
err = PTR_ERR_OR_ZERO(alias);
|
||||
alias = _nfs4_proc_mkdir(dir, dentry, sattr, label, &err);
|
||||
trace_nfs4_mkdir(dir, &dentry->d_name, err);
|
||||
err = nfs4_handle_exception(NFS_SERVER(dir), err,
|
||||
&exception);
|
||||
if (err)
|
||||
alias = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
|
||||
err,
|
||||
&exception));
|
||||
} while (exception.retry);
|
||||
nfs4_label_release_security(label);
|
||||
|
||||
|
|
@ -6211,6 +6219,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen,
|
|||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
int ret;
|
||||
|
||||
if (unlikely(NFS_FH(inode)->size == 0))
|
||||
return -ENODATA;
|
||||
if (!nfs4_server_supports_acls(server, type))
|
||||
return -EOPNOTSUPP;
|
||||
ret = nfs_revalidate_inode(inode, NFS_INO_INVALID_CHANGE);
|
||||
|
|
@ -6285,6 +6295,9 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf,
|
|||
{
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
|
||||
if (unlikely(NFS_FH(inode)->size == 0))
|
||||
return -ENODATA;
|
||||
do {
|
||||
err = __nfs4_proc_set_acl(inode, buf, buflen, type);
|
||||
trace_nfs4_set_acl(inode, err);
|
||||
|
|
@ -10611,7 +10624,7 @@ static const struct rpc_call_ops nfs41_free_stateid_ops = {
|
|||
* Note: this function is always asynchronous.
|
||||
*/
|
||||
static int nfs41_free_stateid(struct nfs_server *server,
|
||||
const nfs4_stateid *stateid,
|
||||
nfs4_stateid *stateid,
|
||||
const struct cred *cred,
|
||||
bool privileged)
|
||||
{
|
||||
|
|
@ -10651,6 +10664,7 @@ static int nfs41_free_stateid(struct nfs_server *server,
|
|||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
rpc_put_task(task);
|
||||
stateid->type = NFS4_FREED_STATEID_TYPE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -10817,6 +10831,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
|
|||
| NFS_CAP_OFFLOAD_CANCEL
|
||||
| NFS_CAP_COPY_NOTIFY
|
||||
| NFS_CAP_DEALLOCATE
|
||||
| NFS_CAP_ZERO_RANGE
|
||||
| NFS_CAP_SEEK
|
||||
| NFS_CAP_LAYOUTSTATS
|
||||
| NFS_CAP_CLONE
|
||||
|
|
@ -10852,7 +10867,7 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
|
|||
|
||||
static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
{
|
||||
ssize_t error, error2, error3;
|
||||
ssize_t error, error2, error3, error4;
|
||||
size_t left = size;
|
||||
|
||||
error = generic_listxattr(dentry, list, left);
|
||||
|
|
@ -10875,8 +10890,16 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
|
|||
error3 = nfs4_listxattr_nfs4_user(d_inode(dentry), list, left);
|
||||
if (error3 < 0)
|
||||
return error3;
|
||||
if (list) {
|
||||
list += error3;
|
||||
left -= error3;
|
||||
}
|
||||
|
||||
error += error2 + error3;
|
||||
error4 = security_inode_listsecurity(d_inode(dentry), list, left);
|
||||
if (error4 < 0)
|
||||
return error4;
|
||||
|
||||
error += error2 + error3 + error4;
|
||||
if (size && error > size)
|
||||
return -ERANGE;
|
||||
return error;
|
||||
|
|
|
|||
|
|
@ -7711,6 +7711,7 @@ const struct rpc_procinfo nfs4_procedures[] = {
|
|||
PROC42(LISTXATTRS, enc_listxattrs, dec_listxattrs),
|
||||
PROC42(REMOVEXATTR, enc_removexattr, dec_removexattr),
|
||||
PROC42(READ_PLUS, enc_read_plus, dec_read_plus),
|
||||
PROC42(ZERO_RANGE, enc_zero_range, dec_zero_range),
|
||||
};
|
||||
|
||||
static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)];
|
||||
|
|
|
|||
|
|
@ -830,10 +830,16 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
|
|||
.servername = clp->cl_hostname,
|
||||
.connect_timeout = connect_timeout,
|
||||
.reconnect_timeout = connect_timeout,
|
||||
.xprtsec = clp->cl_xprtsec,
|
||||
};
|
||||
|
||||
if (da->da_transport != clp->cl_proto)
|
||||
if (da->da_transport != clp->cl_proto &&
|
||||
clp->cl_proto != XPRT_TRANSPORT_TCP_TLS)
|
||||
continue;
|
||||
if (da->da_transport == XPRT_TRANSPORT_TCP &&
|
||||
mds_srv->nfs_client->cl_proto == XPRT_TRANSPORT_TCP_TLS)
|
||||
xprt_args.ident = XPRT_TRANSPORT_TCP_TLS;
|
||||
|
||||
if (da->da_addr.ss_family != clp->cl_addr.ss_family)
|
||||
continue;
|
||||
/* Add this address as an alias */
|
||||
|
|
@ -841,6 +847,9 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
|
|||
rpc_clnt_test_and_add_xprt, NULL);
|
||||
continue;
|
||||
}
|
||||
if (da->da_transport == XPRT_TRANSPORT_TCP &&
|
||||
mds_srv->nfs_client->cl_proto == XPRT_TRANSPORT_TCP_TLS)
|
||||
da->da_transport = XPRT_TRANSPORT_TCP_TLS;
|
||||
clp = get_v3_ds_connect(mds_srv,
|
||||
&da->da_addr,
|
||||
da->da_addrlen, da->da_transport,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ static int nfs_return_empty_folio(struct folio *folio)
|
|||
{
|
||||
folio_zero_segment(folio, 0, folio_size(folio));
|
||||
folio_mark_uptodate(folio);
|
||||
if (nfs_netfs_folio_unlock(folio))
|
||||
folio_unlock(folio);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1051,6 +1051,16 @@ 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
|
||||
|
|
@ -1308,8 +1318,17 @@ 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)
|
||||
|
|
|
|||
|
|
@ -387,6 +387,33 @@ static inline void nfs_sysfs_add_nfsv41_server(struct nfs_server *server)
|
|||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
|
||||
static ssize_t
|
||||
localio_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct nfs_server *server = container_of(kobj, struct nfs_server, kobj);
|
||||
bool localio = nfs_server_is_local(server->nfs_client);
|
||||
return sysfs_emit(buf, "%d\n", localio);
|
||||
}
|
||||
|
||||
static struct kobj_attribute nfs_sysfs_attr_localio = __ATTR_RO(localio);
|
||||
|
||||
static void nfs_sysfs_add_nfs_localio_server(struct nfs_server *server)
|
||||
{
|
||||
int ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_localio.attr,
|
||||
nfs_netns_server_namespace(&server->kobj));
|
||||
if (ret < 0)
|
||||
pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
|
||||
server->s_sysfs_id, ret);
|
||||
}
|
||||
#else
|
||||
static inline void nfs_sysfs_add_nfs_localio_server(struct nfs_server *server)
|
||||
{
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_NFS_LOCALIO) */
|
||||
|
||||
void nfs_sysfs_add_server(struct nfs_server *server)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -405,6 +432,7 @@ void nfs_sysfs_add_server(struct nfs_server *server)
|
|||
server->s_sysfs_id, ret);
|
||||
|
||||
nfs_sysfs_add_nfsv41_server(server);
|
||||
nfs_sysfs_add_nfs_localio_server(server);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_sysfs_add_server);
|
||||
|
||||
|
|
|
|||
|
|
@ -632,19 +632,19 @@ static void nfs_write_error(struct nfs_page *req, int error)
|
|||
* Find an associated nfs write request, and prepare to flush it out
|
||||
* May return an error if the user signalled nfs_wait_on_request().
|
||||
*/
|
||||
static int nfs_page_async_flush(struct folio *folio,
|
||||
struct writeback_control *wbc,
|
||||
static int nfs_do_writepage(struct folio *folio, struct writeback_control *wbc,
|
||||
struct nfs_pageio_descriptor *pgio)
|
||||
{
|
||||
struct nfs_page *req;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
nfs_pageio_cond_complete(pgio, folio->index);
|
||||
|
||||
req = nfs_lock_and_join_requests(folio);
|
||||
if (!req)
|
||||
goto out;
|
||||
ret = PTR_ERR(req);
|
||||
return 0;
|
||||
if (IS_ERR(req))
|
||||
goto out;
|
||||
return PTR_ERR(req);
|
||||
|
||||
nfs_folio_set_writeback(folio);
|
||||
WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));
|
||||
|
|
@ -654,7 +654,6 @@ static int nfs_page_async_flush(struct folio *folio,
|
|||
if (nfs_error_is_fatal_on_server(ret))
|
||||
goto out_launder;
|
||||
|
||||
ret = 0;
|
||||
if (!nfs_pageio_add_request(pgio, req)) {
|
||||
ret = pgio->pg_error;
|
||||
/*
|
||||
|
|
@ -662,28 +661,20 @@ static int nfs_page_async_flush(struct folio *folio,
|
|||
*/
|
||||
if (nfs_error_is_fatal_on_server(ret))
|
||||
goto out_launder;
|
||||
if (wbc->sync_mode == WB_SYNC_NONE)
|
||||
ret = AOP_WRITEPAGE_ACTIVATE;
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
nfs_redirty_request(req);
|
||||
pgio->pg_error = 0;
|
||||
} else
|
||||
nfs_add_stats(folio->mapping->host,
|
||||
NFSIOS_WRITEPAGES, 1);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
nfs_add_stats(folio->mapping->host, NFSIOS_WRITEPAGES, 1);
|
||||
return 0;
|
||||
|
||||
out_launder:
|
||||
nfs_write_error(req, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_do_writepage(struct folio *folio, struct writeback_control *wbc,
|
||||
struct nfs_pageio_descriptor *pgio)
|
||||
{
|
||||
nfs_pageio_cond_complete(pgio, folio->index);
|
||||
return nfs_page_async_flush(folio, wbc, pgio);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write an mmapped page to the server.
|
||||
*/
|
||||
|
|
@ -703,17 +694,6 @@ static int nfs_writepage_locked(struct folio *folio,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int nfs_writepages_callback(struct folio *folio,
|
||||
struct writeback_control *wbc, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nfs_do_writepage(folio, wbc, data);
|
||||
if (ret != AOP_WRITEPAGE_ACTIVATE)
|
||||
folio_unlock(folio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nfs_io_completion_commit(void *inode)
|
||||
{
|
||||
nfs_commit_inode(inode, 0);
|
||||
|
|
@ -749,11 +729,15 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
|
|||
}
|
||||
|
||||
do {
|
||||
struct folio *folio = NULL;
|
||||
|
||||
nfs_pageio_init_write(&pgio, inode, priority, false,
|
||||
&nfs_async_write_completion_ops);
|
||||
pgio.pg_io_completion = ioc;
|
||||
err = write_cache_pages(mapping, wbc, nfs_writepages_callback,
|
||||
&pgio);
|
||||
while ((folio = writeback_iter(mapping, wbc, folio, &err))) {
|
||||
err = nfs_do_writepage(folio, wbc, &pgio);
|
||||
folio_unlock(folio);
|
||||
}
|
||||
pgio.pg_error = 0;
|
||||
nfs_pageio_complete(&pgio);
|
||||
if (err == -EAGAIN && mntflags & NFS_MOUNT_SOFTERR)
|
||||
|
|
|
|||
|
|
@ -151,8 +151,7 @@ EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
|
|||
*/
|
||||
static bool nfs_uuid_put(nfs_uuid_t *nfs_uuid)
|
||||
{
|
||||
LIST_HEAD(local_files);
|
||||
struct nfs_file_localio *nfl, *tmp;
|
||||
struct nfs_file_localio *nfl;
|
||||
|
||||
spin_lock(&nfs_uuid->lock);
|
||||
if (unlikely(!rcu_access_pointer(nfs_uuid->net))) {
|
||||
|
|
@ -166,17 +165,42 @@ static bool nfs_uuid_put(nfs_uuid_t *nfs_uuid)
|
|||
nfs_uuid->dom = NULL;
|
||||
}
|
||||
|
||||
list_splice_init(&nfs_uuid->files, &local_files);
|
||||
spin_unlock(&nfs_uuid->lock);
|
||||
|
||||
/* Walk list of files and ensure their last references dropped */
|
||||
list_for_each_entry_safe(nfl, tmp, &local_files, list) {
|
||||
nfs_close_local_fh(nfl);
|
||||
cond_resched();
|
||||
|
||||
while ((nfl = list_first_entry_or_null(&nfs_uuid->files,
|
||||
struct nfs_file_localio,
|
||||
list)) != NULL) {
|
||||
/* If nfs_uuid is already NULL, nfs_close_local_fh is
|
||||
* closing and we must wait, else we unlink and close.
|
||||
*/
|
||||
if (rcu_access_pointer(nfl->nfs_uuid) == NULL) {
|
||||
/* nfs_close_local_fh() is doing the
|
||||
* close and we must wait. until it unlinks
|
||||
*/
|
||||
wait_var_event_spinlock(nfl,
|
||||
list_first_entry_or_null(
|
||||
&nfs_uuid->files,
|
||||
struct nfs_file_localio,
|
||||
list) != nfl,
|
||||
&nfs_uuid->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Remove nfl from nfs_uuid->files list */
|
||||
list_del_init(&nfl->list);
|
||||
spin_unlock(&nfs_uuid->lock);
|
||||
|
||||
nfs_to_nfsd_file_put_local(&nfl->ro_file);
|
||||
nfs_to_nfsd_file_put_local(&nfl->rw_file);
|
||||
cond_resched();
|
||||
|
||||
spin_lock(&nfs_uuid->lock);
|
||||
BUG_ON(!list_empty(&nfs_uuid->files));
|
||||
/* Now we can allow racing nfs_close_local_fh() to
|
||||
* skip the locking.
|
||||
*/
|
||||
RCU_INIT_POINTER(nfl->nfs_uuid, NULL);
|
||||
wake_up_var_locked(&nfl->nfs_uuid, &nfs_uuid->lock);
|
||||
}
|
||||
|
||||
/* Remove client from nn->local_clients */
|
||||
if (nfs_uuid->list_lock) {
|
||||
|
|
@ -237,6 +261,7 @@ static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl
|
|||
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
|
||||
struct rpc_clnt *rpc_clnt, const struct cred *cred,
|
||||
const struct nfs_fh *nfs_fh, struct nfs_file_localio *nfl,
|
||||
struct nfsd_file __rcu **pnf,
|
||||
const fmode_t fmode)
|
||||
{
|
||||
struct net *net;
|
||||
|
|
@ -261,10 +286,9 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
|
|||
rcu_read_unlock();
|
||||
/* We have an implied reference to net thanks to nfsd_net_try_get */
|
||||
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
|
||||
cred, nfs_fh, fmode);
|
||||
if (IS_ERR(localio))
|
||||
cred, nfs_fh, pnf, fmode);
|
||||
nfs_to_nfsd_net_put(net);
|
||||
else
|
||||
if (!IS_ERR(localio))
|
||||
nfs_uuid_add_file(uuid, nfl);
|
||||
|
||||
return localio;
|
||||
|
|
@ -273,8 +297,6 @@ EXPORT_SYMBOL_GPL(nfs_open_local_fh);
|
|||
|
||||
void nfs_close_local_fh(struct nfs_file_localio *nfl)
|
||||
{
|
||||
struct nfsd_file *ro_nf = NULL;
|
||||
struct nfsd_file *rw_nf = NULL;
|
||||
nfs_uuid_t *nfs_uuid;
|
||||
|
||||
rcu_read_lock();
|
||||
|
|
@ -285,28 +307,39 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
|
|||
return;
|
||||
}
|
||||
|
||||
ro_nf = rcu_access_pointer(nfl->ro_file);
|
||||
rw_nf = rcu_access_pointer(nfl->rw_file);
|
||||
if (ro_nf || rw_nf) {
|
||||
spin_lock(&nfs_uuid->lock);
|
||||
if (ro_nf)
|
||||
ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
|
||||
if (rw_nf)
|
||||
rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
|
||||
|
||||
/* Remove nfl from nfs_uuid->files list */
|
||||
if (!rcu_access_pointer(nfl->nfs_uuid)) {
|
||||
/* nfs_uuid_put has finished here */
|
||||
spin_unlock(&nfs_uuid->lock);
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
if (list_empty(&nfs_uuid->files)) {
|
||||
/* nfs_uuid_put() has started closing files, wait for it
|
||||
* to finished
|
||||
*/
|
||||
spin_unlock(&nfs_uuid->lock);
|
||||
rcu_read_unlock();
|
||||
wait_var_event(&nfl->nfs_uuid,
|
||||
rcu_access_pointer(nfl->nfs_uuid) == NULL);
|
||||
return;
|
||||
}
|
||||
/* tell nfs_uuid_put() to wait for us */
|
||||
RCU_INIT_POINTER(nfl->nfs_uuid, NULL);
|
||||
list_del_init(&nfl->list);
|
||||
spin_unlock(&nfs_uuid->lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (ro_nf)
|
||||
nfs_to_nfsd_file_put_local(ro_nf);
|
||||
if (rw_nf)
|
||||
nfs_to_nfsd_file_put_local(rw_nf);
|
||||
return;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
nfs_to_nfsd_file_put_local(&nfl->ro_file);
|
||||
nfs_to_nfsd_file_put_local(&nfl->rw_file);
|
||||
|
||||
/* Remove nfl from nfs_uuid->files list and signal nfs_uuid_put()
|
||||
* that we are done. The moment we drop the spinlock the
|
||||
* nfs_uuid could be freed.
|
||||
*/
|
||||
spin_lock(&nfs_uuid->lock);
|
||||
list_del_init(&nfl->list);
|
||||
wake_up_var_locked(&nfl->nfs_uuid, &nfs_uuid->lock);
|
||||
spin_unlock(&nfs_uuid->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_close_local_fh);
|
||||
|
||||
|
|
|
|||
|
|
@ -378,12 +378,38 @@ nfsd_file_put(struct nfsd_file *nf)
|
|||
* the reference of the nfsd_file.
|
||||
*/
|
||||
struct net *
|
||||
nfsd_file_put_local(struct nfsd_file *nf)
|
||||
nfsd_file_put_local(struct nfsd_file __rcu **pnf)
|
||||
{
|
||||
struct nfsd_file *nf;
|
||||
struct net *net = NULL;
|
||||
|
||||
nf = unrcu_pointer(xchg(pnf, NULL));
|
||||
if (nf) {
|
||||
net = nf->nf_net;
|
||||
nfsd_file_put(nf);
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_file_get_local - get nfsd_file reference and reference to net
|
||||
* @nf: nfsd_file of which to put the reference
|
||||
*
|
||||
* Get reference to both the nfsd_file and nf->nf_net.
|
||||
*/
|
||||
struct nfsd_file *
|
||||
nfsd_file_get_local(struct nfsd_file *nf)
|
||||
{
|
||||
struct net *net = nf->nf_net;
|
||||
|
||||
nfsd_file_put(nf);
|
||||
return net;
|
||||
if (nfsd_net_try_get(net)) {
|
||||
nf = nfsd_file_get(nf);
|
||||
if (!nf)
|
||||
nfsd_net_put(net);
|
||||
} else {
|
||||
nf = NULL;
|
||||
}
|
||||
return nf;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ void nfsd_file_cache_shutdown(void);
|
|||
int nfsd_file_cache_start_net(struct net *net);
|
||||
void nfsd_file_cache_shutdown_net(struct net *net);
|
||||
void nfsd_file_put(struct nfsd_file *nf);
|
||||
struct net *nfsd_file_put_local(struct nfsd_file *nf);
|
||||
struct net *nfsd_file_put_local(struct nfsd_file __rcu **nf);
|
||||
struct nfsd_file *nfsd_file_get_local(struct nfsd_file *nf);
|
||||
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
|
||||
struct file *nfsd_file_file(struct nfsd_file *nf);
|
||||
void nfsd_file_close_inode_sync(struct inode *inode);
|
||||
|
|
|
|||
|
|
@ -24,21 +24,6 @@
|
|||
#include "filecache.h"
|
||||
#include "cache.h"
|
||||
|
||||
static const struct nfsd_localio_operations nfsd_localio_ops = {
|
||||
.nfsd_net_try_get = nfsd_net_try_get,
|
||||
.nfsd_net_put = nfsd_net_put,
|
||||
.nfsd_open_local_fh = nfsd_open_local_fh,
|
||||
.nfsd_file_put_local = nfsd_file_put_local,
|
||||
.nfsd_file_get = nfsd_file_get,
|
||||
.nfsd_file_put = nfsd_file_put,
|
||||
.nfsd_file_file = nfsd_file_file,
|
||||
};
|
||||
|
||||
void nfsd_localio_ops_init(void)
|
||||
{
|
||||
nfs_to = &nfsd_localio_ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_open_local_fh - lookup a local filehandle @nfs_fh and map to nfsd_file
|
||||
*
|
||||
|
|
@ -47,6 +32,7 @@ void nfsd_localio_ops_init(void)
|
|||
* @rpc_clnt: rpc_clnt that the client established
|
||||
* @cred: cred that the client established
|
||||
* @nfs_fh: filehandle to lookup
|
||||
* @nfp: place to find the nfsd_file, or store it if it was non-NULL
|
||||
* @fmode: fmode_t to use for open
|
||||
*
|
||||
* This function maps a local fh to a path on a local filesystem.
|
||||
|
|
@ -57,10 +43,11 @@ void nfsd_localio_ops_init(void)
|
|||
* set. Caller (NFS client) is responsible for calling nfsd_net_put and
|
||||
* nfsd_file_put (via nfs_to_nfsd_file_put_local).
|
||||
*/
|
||||
struct nfsd_file *
|
||||
static struct nfsd_file *
|
||||
nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
|
||||
struct rpc_clnt *rpc_clnt, const struct cred *cred,
|
||||
const struct nfs_fh *nfs_fh, const fmode_t fmode)
|
||||
const struct nfs_fh *nfs_fh, struct nfsd_file __rcu **pnf,
|
||||
const fmode_t fmode)
|
||||
{
|
||||
int mayflags = NFSD_MAY_LOCALIO;
|
||||
struct svc_cred rq_cred;
|
||||
|
|
@ -71,6 +58,15 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
|
|||
if (nfs_fh->size > NFS4_FHSIZE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!nfsd_net_try_get(net))
|
||||
return ERR_PTR(-ENXIO);
|
||||
|
||||
rcu_read_lock();
|
||||
localio = nfsd_file_get(rcu_dereference(*pnf));
|
||||
rcu_read_unlock();
|
||||
if (localio)
|
||||
return localio;
|
||||
|
||||
/* nfs_fh -> svc_fh */
|
||||
fh_init(&fh, NFS4_FHSIZE);
|
||||
fh.fh_handle.fh_size = nfs_fh->size;
|
||||
|
|
@ -92,9 +88,47 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
|
|||
if (rq_cred.cr_group_info)
|
||||
put_group_info(rq_cred.cr_group_info);
|
||||
|
||||
if (!IS_ERR(localio)) {
|
||||
struct nfsd_file *new;
|
||||
if (!nfsd_net_try_get(net)) {
|
||||
nfsd_file_put(localio);
|
||||
nfsd_net_put(net);
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
nfsd_file_get(localio);
|
||||
again:
|
||||
new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio)));
|
||||
if (new) {
|
||||
/* Some other thread installed an nfsd_file */
|
||||
if (nfsd_file_get(new) == NULL)
|
||||
goto again;
|
||||
/*
|
||||
* Drop the ref we were going to install and the
|
||||
* one we were going to return.
|
||||
*/
|
||||
nfsd_file_put(localio);
|
||||
nfsd_file_put(localio);
|
||||
localio = new;
|
||||
}
|
||||
} else
|
||||
nfsd_net_put(net);
|
||||
|
||||
return localio;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfsd_open_local_fh);
|
||||
|
||||
static const struct nfsd_localio_operations nfsd_localio_ops = {
|
||||
.nfsd_net_try_get = nfsd_net_try_get,
|
||||
.nfsd_net_put = nfsd_net_put,
|
||||
.nfsd_open_local_fh = nfsd_open_local_fh,
|
||||
.nfsd_file_put_local = nfsd_file_put_local,
|
||||
.nfsd_file_get_local = nfsd_file_get_local,
|
||||
.nfsd_file_file = nfsd_file_file,
|
||||
};
|
||||
|
||||
void nfsd_localio_ops_init(void)
|
||||
{
|
||||
nfs_to = &nfsd_localio_ops;
|
||||
}
|
||||
|
||||
/*
|
||||
* UUID_IS_LOCAL XDR functions
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ struct nfs4_stateid_struct {
|
|||
NFS4_LAYOUT_STATEID_TYPE,
|
||||
NFS4_PNFS_DS_STATEID_TYPE,
|
||||
NFS4_REVOKED_STATEID_TYPE,
|
||||
NFS4_FREED_STATEID_TYPE,
|
||||
} type;
|
||||
};
|
||||
|
||||
|
|
@ -678,6 +679,7 @@ enum {
|
|||
NFSPROC4_CLNT_SEEK,
|
||||
NFSPROC4_CLNT_ALLOCATE,
|
||||
NFSPROC4_CLNT_DEALLOCATE,
|
||||
NFSPROC4_CLNT_ZERO_RANGE,
|
||||
NFSPROC4_CLNT_LAYOUTSTATS,
|
||||
NFSPROC4_CLNT_CLONE,
|
||||
NFSPROC4_CLNT_COPY,
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ struct nfs_client {
|
|||
*/
|
||||
char cl_ipaddr[48];
|
||||
struct net *cl_net;
|
||||
netns_tracker cl_ns_tracker;
|
||||
struct list_head pending_cb_stateids;
|
||||
struct rcu_head rcu;
|
||||
|
||||
|
|
@ -303,6 +304,7 @@ struct nfs_server {
|
|||
#define NFS_CAP_CASE_PRESERVING (1U << 7)
|
||||
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
|
||||
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
|
||||
#define NFS_CAP_ZERO_RANGE (1U << 10)
|
||||
#define NFS_CAP_OPEN_XOR (1U << 12)
|
||||
#define NFS_CAP_DELEGTIME (1U << 13)
|
||||
#define NFS_CAP_POSIX_LOCK (1U << 14)
|
||||
|
|
|
|||
|
|
@ -50,10 +50,6 @@ void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
|
|||
spinlock_t *nn_local_clients_lock);
|
||||
|
||||
/* localio needs to map filehandle -> struct nfsd_file */
|
||||
extern struct nfsd_file *
|
||||
nfsd_open_local_fh(struct net *, struct auth_domain *, struct rpc_clnt *,
|
||||
const struct cred *, const struct nfs_fh *,
|
||||
const fmode_t) __must_hold(rcu);
|
||||
void nfs_close_local_fh(struct nfs_file_localio *);
|
||||
|
||||
struct nfsd_localio_operations {
|
||||
|
|
@ -64,10 +60,10 @@ struct nfsd_localio_operations {
|
|||
struct rpc_clnt *,
|
||||
const struct cred *,
|
||||
const struct nfs_fh *,
|
||||
struct nfsd_file __rcu **pnf,
|
||||
const fmode_t);
|
||||
struct net *(*nfsd_file_put_local)(struct nfsd_file *);
|
||||
struct nfsd_file *(*nfsd_file_get)(struct nfsd_file *);
|
||||
void (*nfsd_file_put)(struct nfsd_file *);
|
||||
struct net *(*nfsd_file_put_local)(struct nfsd_file __rcu **);
|
||||
struct nfsd_file *(*nfsd_file_get_local)(struct nfsd_file *);
|
||||
struct file *(*nfsd_file_file)(struct nfsd_file *);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
|
|
@ -77,6 +73,7 @@ extern const struct nfsd_localio_operations *nfs_to;
|
|||
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
|
||||
struct rpc_clnt *, const struct cred *,
|
||||
const struct nfs_fh *, struct nfs_file_localio *,
|
||||
struct nfsd_file __rcu **pnf,
|
||||
const fmode_t);
|
||||
|
||||
static inline void nfs_to_nfsd_net_put(struct net *net)
|
||||
|
|
@ -91,15 +88,18 @@ static inline void nfs_to_nfsd_net_put(struct net *net)
|
|||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
|
||||
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file __rcu **localio)
|
||||
{
|
||||
/*
|
||||
* Must not hold RCU otherwise nfsd_file_put() can easily trigger:
|
||||
* "Voluntary context switch within RCU read-side critical section!"
|
||||
* by scheduling deep in underlying filesystem (e.g. XFS).
|
||||
* Either *localio must be guaranteed to be non-NULL, or caller
|
||||
* must prevent nfsd shutdown from completing as nfs_close_local_fh()
|
||||
* does by blocking the nfs_uuid from being finally put.
|
||||
*/
|
||||
struct net *net = nfs_to->nfsd_file_put_local(localio);
|
||||
struct net *net;
|
||||
|
||||
net = nfs_to->nfsd_file_put_local(localio);
|
||||
|
||||
if (net)
|
||||
nfs_to_nfsd_net_put(net);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#define RPC_MAXCWND(xprt) ((xprt)->max_reqs << RPC_CWNDSHIFT)
|
||||
#define RPCXPRT_CONGESTED(xprt) ((xprt)->cong >= (xprt)->cwnd)
|
||||
|
||||
#define RPC_GSS_SEQNO_ARRAY_SIZE 3U
|
||||
|
||||
enum rpc_display_format_t {
|
||||
RPC_DISPLAY_ADDR = 0,
|
||||
RPC_DISPLAY_PORT,
|
||||
|
|
@ -66,7 +68,8 @@ struct rpc_rqst {
|
|||
struct rpc_cred * rq_cred; /* Bound cred */
|
||||
__be32 rq_xid; /* request XID */
|
||||
int rq_cong; /* has incremented xprt->cong */
|
||||
u32 rq_seqno; /* gss seq no. used on req. */
|
||||
u32 rq_seqnos[RPC_GSS_SEQNO_ARRAY_SIZE]; /* past gss req seq nos. */
|
||||
unsigned int rq_seqno_count; /* number of entries in rq_seqnos */
|
||||
int rq_enc_pages_num;
|
||||
struct page **rq_enc_pages; /* scratch pages for use by
|
||||
gss privacy code */
|
||||
|
|
@ -119,6 +122,18 @@ struct rpc_rqst {
|
|||
#define rq_svec rq_snd_buf.head
|
||||
#define rq_slen rq_snd_buf.len
|
||||
|
||||
static inline int xprt_rqst_add_seqno(struct rpc_rqst *req, u32 seqno)
|
||||
{
|
||||
if (likely(req->rq_seqno_count < RPC_GSS_SEQNO_ARRAY_SIZE))
|
||||
req->rq_seqno_count++;
|
||||
|
||||
/* Shift array to make room for the newest element at the beginning */
|
||||
memmove(&req->rq_seqnos[1], &req->rq_seqnos[0],
|
||||
(RPC_GSS_SEQNO_ARRAY_SIZE - 1) * sizeof(req->rq_seqnos[0]));
|
||||
req->rq_seqnos[0] = seqno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RPC transport layer security policies */
|
||||
enum xprtsec_policies {
|
||||
RPC_XPRTSEC_NONE = 0,
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ TRACE_EVENT(rpcgss_seqno,
|
|||
__entry->task_id = task->tk_pid;
|
||||
__entry->client_id = task->tk_client->cl_clid;
|
||||
__entry->xid = be32_to_cpu(rqst->rq_xid);
|
||||
__entry->seqno = rqst->rq_seqno;
|
||||
__entry->seqno = *rqst->rq_seqnos;
|
||||
),
|
||||
|
||||
TP_printk(SUNRPC_TRACE_TASK_SPECIFIER " xid=0x%08x seqno=%u",
|
||||
|
|
@ -440,7 +440,7 @@ TRACE_EVENT(rpcgss_need_reencode,
|
|||
__entry->client_id = task->tk_client->cl_clid;
|
||||
__entry->xid = be32_to_cpu(task->tk_rqstp->rq_xid);
|
||||
__entry->seq_xmit = seq_xmit;
|
||||
__entry->seqno = task->tk_rqstp->rq_seqno;
|
||||
__entry->seqno = *task->tk_rqstp->rq_seqnos;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
|
|
|
|||
|
|
@ -1098,7 +1098,7 @@ TRACE_EVENT(xprt_transmit,
|
|||
__entry->client_id = rqst->rq_task->tk_client ?
|
||||
rqst->rq_task->tk_client->cl_clid : -1;
|
||||
__entry->xid = be32_to_cpu(rqst->rq_xid);
|
||||
__entry->seqno = rqst->rq_seqno;
|
||||
__entry->seqno = *rqst->rq_seqnos;
|
||||
__entry->status = status;
|
||||
),
|
||||
|
||||
|
|
|
|||
|
|
@ -1545,6 +1545,7 @@ static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr)
|
|||
struct kvec iov;
|
||||
struct xdr_buf verf_buf;
|
||||
int status;
|
||||
u32 seqno;
|
||||
|
||||
/* Credential */
|
||||
|
||||
|
|
@ -1556,15 +1557,16 @@ static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr)
|
|||
cred_len = p++;
|
||||
|
||||
spin_lock(&ctx->gc_seq_lock);
|
||||
req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ;
|
||||
seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ;
|
||||
xprt_rqst_add_seqno(req, seqno);
|
||||
spin_unlock(&ctx->gc_seq_lock);
|
||||
if (req->rq_seqno == MAXSEQ)
|
||||
if (*req->rq_seqnos == MAXSEQ)
|
||||
goto expired;
|
||||
trace_rpcgss_seqno(task);
|
||||
|
||||
*p++ = cpu_to_be32(RPC_GSS_VERSION);
|
||||
*p++ = cpu_to_be32(ctx->gc_proc);
|
||||
*p++ = cpu_to_be32(req->rq_seqno);
|
||||
*p++ = cpu_to_be32(*req->rq_seqnos);
|
||||
*p++ = cpu_to_be32(gss_cred->gc_service);
|
||||
p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
|
||||
*cred_len = cpu_to_be32((p - (cred_len + 1)) << 2);
|
||||
|
|
@ -1678,17 +1680,31 @@ gss_refresh_null(struct rpc_task *task)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_validate_seqno_mic(struct gss_cl_ctx *ctx, u32 seqno, __be32 *seq, __be32 *p, u32 len)
|
||||
{
|
||||
struct kvec iov;
|
||||
struct xdr_buf verf_buf;
|
||||
struct xdr_netobj mic;
|
||||
|
||||
*seq = cpu_to_be32(seqno);
|
||||
iov.iov_base = seq;
|
||||
iov.iov_len = 4;
|
||||
xdr_buf_from_iov(&iov, &verf_buf);
|
||||
mic.data = (u8 *)p;
|
||||
mic.len = len;
|
||||
return gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
|
||||
}
|
||||
|
||||
static int
|
||||
gss_validate(struct rpc_task *task, struct xdr_stream *xdr)
|
||||
{
|
||||
struct rpc_cred *cred = task->tk_rqstp->rq_cred;
|
||||
struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
|
||||
__be32 *p, *seq = NULL;
|
||||
struct kvec iov;
|
||||
struct xdr_buf verf_buf;
|
||||
struct xdr_netobj mic;
|
||||
u32 len, maj_stat;
|
||||
int status;
|
||||
int i = 1; /* don't recheck the first item */
|
||||
|
||||
p = xdr_inline_decode(xdr, 2 * sizeof(*p));
|
||||
if (!p)
|
||||
|
|
@ -1705,13 +1721,10 @@ gss_validate(struct rpc_task *task, struct xdr_stream *xdr)
|
|||
seq = kmalloc(4, GFP_KERNEL);
|
||||
if (!seq)
|
||||
goto validate_failed;
|
||||
*seq = cpu_to_be32(task->tk_rqstp->rq_seqno);
|
||||
iov.iov_base = seq;
|
||||
iov.iov_len = 4;
|
||||
xdr_buf_from_iov(&iov, &verf_buf);
|
||||
mic.data = (u8 *)p;
|
||||
mic.len = len;
|
||||
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
|
||||
maj_stat = gss_validate_seqno_mic(ctx, task->tk_rqstp->rq_seqnos[0], seq, p, len);
|
||||
/* RFC 2203 5.3.3.1 - compute the checksum of each sequence number in the cache */
|
||||
while (unlikely(maj_stat == GSS_S_BAD_SIG && i < task->tk_rqstp->rq_seqno_count))
|
||||
maj_stat = gss_validate_seqno_mic(ctx, task->tk_rqstp->rq_seqnos[i], seq, p, len);
|
||||
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
|
||||
clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
|
||||
if (maj_stat)
|
||||
|
|
@ -1750,7 +1763,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
|
|||
if (!p)
|
||||
goto wrap_failed;
|
||||
integ_len = p++;
|
||||
*p = cpu_to_be32(rqstp->rq_seqno);
|
||||
*p = cpu_to_be32(*rqstp->rq_seqnos);
|
||||
|
||||
if (rpcauth_wrap_req_encode(task, xdr))
|
||||
goto wrap_failed;
|
||||
|
|
@ -1847,7 +1860,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
|
|||
if (!p)
|
||||
goto wrap_failed;
|
||||
opaque_len = p++;
|
||||
*p = cpu_to_be32(rqstp->rq_seqno);
|
||||
*p = cpu_to_be32(*rqstp->rq_seqnos);
|
||||
|
||||
if (rpcauth_wrap_req_encode(task, xdr))
|
||||
goto wrap_failed;
|
||||
|
|
@ -2001,7 +2014,7 @@ gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
|
|||
offset = rcv_buf->len - xdr_stream_remaining(xdr);
|
||||
if (xdr_stream_decode_u32(xdr, &seqno))
|
||||
goto unwrap_failed;
|
||||
if (seqno != rqstp->rq_seqno)
|
||||
if (seqno != *rqstp->rq_seqnos)
|
||||
goto bad_seqno;
|
||||
if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len))
|
||||
goto unwrap_failed;
|
||||
|
|
@ -2045,7 +2058,7 @@ unwrap_failed:
|
|||
trace_rpcgss_unwrap_failed(task);
|
||||
goto out;
|
||||
bad_seqno:
|
||||
trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno);
|
||||
trace_rpcgss_bad_seqno(task, *rqstp->rq_seqnos, seqno);
|
||||
goto out;
|
||||
bad_mic:
|
||||
trace_rpcgss_verify_mic(task, maj_stat);
|
||||
|
|
@ -2077,7 +2090,7 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
|
|||
if (maj_stat != GSS_S_COMPLETE)
|
||||
goto bad_unwrap;
|
||||
/* gss_unwrap decrypted the sequence number */
|
||||
if (be32_to_cpup(p++) != rqstp->rq_seqno)
|
||||
if (be32_to_cpup(p++) != *rqstp->rq_seqnos)
|
||||
goto bad_seqno;
|
||||
|
||||
/* gss_unwrap redacts the opaque blob from the head iovec.
|
||||
|
|
@ -2093,7 +2106,7 @@ unwrap_failed:
|
|||
trace_rpcgss_unwrap_failed(task);
|
||||
return -EIO;
|
||||
bad_seqno:
|
||||
trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p));
|
||||
trace_rpcgss_bad_seqno(task, *rqstp->rq_seqnos, be32_to_cpup(--p));
|
||||
return -EIO;
|
||||
bad_unwrap:
|
||||
trace_rpcgss_unwrap(task, maj_stat);
|
||||
|
|
@ -2118,14 +2131,14 @@ gss_xmit_need_reencode(struct rpc_task *task)
|
|||
if (!ctx)
|
||||
goto out;
|
||||
|
||||
if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq)))
|
||||
if (gss_seq_is_newer(*req->rq_seqnos, READ_ONCE(ctx->gc_seq)))
|
||||
goto out_ctx;
|
||||
|
||||
seq_xmit = READ_ONCE(ctx->gc_seq_xmit);
|
||||
while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) {
|
||||
while (gss_seq_is_newer(*req->rq_seqnos, seq_xmit)) {
|
||||
u32 tmp = seq_xmit;
|
||||
|
||||
seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno);
|
||||
seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, *req->rq_seqnos);
|
||||
if (seq_xmit == tmp) {
|
||||
ret = false;
|
||||
goto out_ctx;
|
||||
|
|
@ -2134,7 +2147,7 @@ gss_xmit_need_reencode(struct rpc_task *task)
|
|||
|
||||
win = ctx->gc_win;
|
||||
if (win > 0)
|
||||
ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win);
|
||||
ret = !gss_seq_is_newer(*req->rq_seqnos, seq_xmit - win);
|
||||
|
||||
out_ctx:
|
||||
gss_put_ctx(ctx);
|
||||
|
|
|
|||
|
|
@ -2771,8 +2771,13 @@ out_verifier:
|
|||
case -EPROTONOSUPPORT:
|
||||
goto out_err;
|
||||
case -EACCES:
|
||||
/* Re-encode with a fresh cred */
|
||||
fallthrough;
|
||||
/* possible RPCSEC_GSS out-of-sequence event (RFC2203),
|
||||
* reset recv state and keep waiting, don't retransmit
|
||||
*/
|
||||
task->tk_rqstp->rq_reply_bytes_recvd = 0;
|
||||
task->tk_status = xprt_request_enqueue_receive(task);
|
||||
task->tk_action = call_transmit_status;
|
||||
return -EBADMSG;
|
||||
default:
|
||||
goto out_garbage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1365,7 +1365,7 @@ xprt_request_enqueue_transmit(struct rpc_task *task)
|
|||
INIT_LIST_HEAD(&req->rq_xmit2);
|
||||
goto out;
|
||||
}
|
||||
} else if (!req->rq_seqno) {
|
||||
} else if (req->rq_seqno_count == 0) {
|
||||
list_for_each_entry(pos, &xprt->xmit_queue, rq_xmit) {
|
||||
if (pos->rq_task->tk_owner != task->tk_owner)
|
||||
continue;
|
||||
|
|
@ -1898,6 +1898,7 @@ xprt_request_init(struct rpc_task *task)
|
|||
req->rq_snd_buf.bvec = NULL;
|
||||
req->rq_rcv_buf.bvec = NULL;
|
||||
req->rq_release_snd_buf = NULL;
|
||||
req->rq_seqno_count = 0;
|
||||
xprt_init_majortimeo(task, req, task->tk_client->cl_timeout);
|
||||
|
||||
trace_xprt_reserve(req);
|
||||
|
|
|
|||
|
|
@ -2726,20 +2726,14 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
|
|||
if (status)
|
||||
goto out_close;
|
||||
xprt_release_write(lower_xprt, NULL);
|
||||
|
||||
trace_rpc_socket_connect(upper_xprt, upper_transport->sock, 0);
|
||||
if (!xprt_test_and_set_connected(upper_xprt)) {
|
||||
upper_xprt->connect_cookie++;
|
||||
clear_bit(XPRT_SOCK_CONNECTING, &upper_transport->sock_state);
|
||||
xprt_clear_connecting(upper_xprt);
|
||||
|
||||
upper_xprt->stat.connect_count++;
|
||||
upper_xprt->stat.connect_time += (long)jiffies -
|
||||
upper_xprt->stat.connect_start;
|
||||
xs_run_error_worker(upper_transport, XPRT_SOCK_WAKE_PENDING);
|
||||
}
|
||||
rpc_shutdown_client(lower_clnt);
|
||||
|
||||
/* Check for ingress data that arrived before the socket's
|
||||
* ->data_ready callback was set up.
|
||||
*/
|
||||
xs_poll_check_readable(upper_transport);
|
||||
|
||||
out_unlock:
|
||||
current_restore_flags(pflags, PF_MEMALLOC);
|
||||
upper_transport->clnt = NULL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue