1
0
mirror of https://github.com/torvalds/linux.git synced 2025-04-12 16:47:42 +00:00

NFSD 6.14 Release Notes

Jeff Layton contributed an implementation of NFSv4.2+ attribute
 delegation, as described here:
 
 https://www.ietf.org/archive/id/draft-ietf-nfsv4-delstid-08.html
 
 This interoperates with similar functionality introduced into the
 Linux NFS client in v6.11. An attribute delegation permits an NFS
 client to manage a file's mtime, rather than flushing dirty data to
 the NFS server so that the file's mtime reflects the last write,
 which is considerably slower.
 
 Neil Brown contributed dynamic NFSv4.1 session slot table resizing.
 This facility enables NFSD to increase or decrease the number of
 slots per NFS session depending on server memory availability. More
 session slots means greater parallelism.
 
 Chuck Lever fixed a long-standing latent bug where NFSv4 COMPOUND
 encoding screws up when crossing a page boundary in the encoding
 buffer. This is a zero-day bug, but hitting it is rare and depends
 on the NFS client implementation. The Linux NFS client does not
 happen to trigger this issue.
 
 A variety of bug fixes and other incremental improvements fill out
 the list of commits in this release. Great thanks to all
 contributors, reviewers, testers, and bug reporters who participated
 during this development cycle.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmeVPBUACgkQM2qzM29m
 f5dClxAAmW4O2bOJaR8neJ54fzeFYXtFYEXRF/XbIh2KdnCy2LoywT9ux8ndzE0k
 1tsjtv0g4y84IcYfrxXPRTuhh2GO2pHw5L1kGVRezJg2ODSFbpdzGtcVK7SIrs2S
 eijTViqdi9xRgfj1jPqRrvxC99RL3/fmCztqorAPLDFsqYAd/6ZRxZ7+IcZ2h4+J
 cJ0Z6Wx6eh10roacZPXweH13XJ7xWO/ublYZvQFQpK2BAKyO98aXGgLDraNt8k60
 X3DZLSKkGB/eBlNeAlTtcrXec/ot6XGJPKr3b/7zhwfMi8B13RGdSmCR8SxMdRQM
 vCQO4G2YadU4YFS6FFIw9Wc1XDYUuYh2YgcveafjzjXbgi7NnY7rYOxtnTgBi0Xv
 XGjtGqpvD676gPm+8b3DcwqmWI3c/WUdtQIZ1uYRCFZdFqkVP91bySPT2aHtx2GO
 4j3uEyTlypC00kyvu1oL3+tVUG/EFlJCpYvIbOwqDG2m7KWPStzpfJTD5Q9cdlEl
 fdgs6l82EVqe1YyjLTqajDuOcRrLYK2hlR/5STc03hQV+GpKSo5UypRejzE9WtRV
 zT/tyelqhj4+0EZJz4ay/8q9s2Jp+5JGVxoVvjujSuH7+Ulb3T+IDkldtMamO8Fm
 www2y0/fLfU2xIapMJdCoJ+ZKgel2i8RZMPIc0cIfO5ITXm+dOs=
 =hwXx
 -----END PGP SIGNATURE-----

Merge tag 'nfsd-6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd updates from Chuck Lever:
 "Jeff Layton contributed an implementation of NFSv4.2+ attribute
  delegation, as described here:

    https://www.ietf.org/archive/id/draft-ietf-nfsv4-delstid-08.html

  This interoperates with similar functionality introduced into the
  Linux NFS client in v6.11. An attribute delegation permits an NFS
  client to manage a file's mtime, rather than flushing dirty data to
  the NFS server so that the file's mtime reflects the last write, which
  is considerably slower.

  Neil Brown contributed dynamic NFSv4.1 session slot table resizing.
  This facility enables NFSD to increase or decrease the number of slots
  per NFS session depending on server memory availability. More session
  slots means greater parallelism.

  Chuck Lever fixed a long-standing latent bug where NFSv4 COMPOUND
  encoding screws up when crossing a page boundary in the encoding
  buffer. This is a zero-day bug, but hitting it is rare and depends on
  the NFS client implementation. The Linux NFS client does not happen to
  trigger this issue.

  A variety of bug fixes and other incremental improvements fill out the
  list of commits in this release. Great thanks to all contributors,
  reviewers, testers, and bug reporters who participated during this
  development cycle"

* tag 'nfsd-6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (42 commits)
  sunrpc: Remove gss_{de,en}crypt_xdr_buf deadcode
  sunrpc: Remove gss_generic_token deadcode
  sunrpc: Remove unused xprt_iter_get_xprt
  Revert "SUNRPC: Reduce thread wake-up rate when receiving large RPC messages"
  nfsd: implement OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION
  nfsd: handle delegated timestamps in SETATTR
  nfsd: add support for delegated timestamps
  nfsd: rework NFS4_SHARE_WANT_* flag handling
  nfsd: add support for FATTR4_OPEN_ARGUMENTS
  nfsd: prepare delegation code for handing out *_ATTRS_DELEG delegations
  nfsd: rename NFS4_SHARE_WANT_* constants to OPEN4_SHARE_ACCESS_WANT_*
  nfsd: switch to autogenerated definitions for open_delegation_type4
  nfs_common: make include/linux/nfs4.h include generated nfs4_1.h
  nfsd: fix handling of delegated change attr in CB_GETATTR
  SUNRPC: Document validity guarantees of the pointer returned by reserve_space
  NFSD: Insulate nfsd4_encode_fattr4() from page boundaries in the encode buffer
  NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the encode buffer
  NFSD: Refactor nfsd4_do_encode_secinfo() again
  NFSD: Insulate nfsd4_encode_readlink() from page boundaries in the encode buffer
  NFSD: Insulate nfsd4_encode_read_plus_data() from page boundaries in the encode buffer
  ...
This commit is contained in:
Linus Torvalds 2025-01-27 17:27:24 -08:00
commit f34b580514
43 changed files with 1462 additions and 885 deletions

@ -0,0 +1,186 @@
/*
* Copyright (c) 2010 IETF Trust and the persons identified
* as the document authors. All rights reserved.
*
* The document authors are identified in RFC 3530 and
* RFC 5661.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* - Neither the name of Internet Society, IETF or IETF
* Trust, nor the names of specific contributors, may be
* used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
pragma header nfs4;
/*
* Basic typedefs for RFC 1832 data type definitions
*/
typedef hyper int64_t;
typedef unsigned int uint32_t;
/*
* Basic data types
*/
typedef uint32_t bitmap4<>;
/*
* Timeval
*/
struct nfstime4 {
int64_t seconds;
uint32_t nseconds;
};
/*
* The following content was extracted from draft-ietf-nfsv4-delstid
*/
typedef bool fattr4_offline;
const FATTR4_OFFLINE = 83;
struct open_arguments4 {
bitmap4 oa_share_access;
bitmap4 oa_share_deny;
bitmap4 oa_share_access_want;
bitmap4 oa_open_claim;
bitmap4 oa_create_mode;
};
enum open_args_share_access4 {
OPEN_ARGS_SHARE_ACCESS_READ = 1,
OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
OPEN_ARGS_SHARE_ACCESS_BOTH = 3
};
enum open_args_share_deny4 {
OPEN_ARGS_SHARE_DENY_NONE = 0,
OPEN_ARGS_SHARE_DENY_READ = 1,
OPEN_ARGS_SHARE_DENY_WRITE = 2,
OPEN_ARGS_SHARE_DENY_BOTH = 3
};
enum open_args_share_access_want4 {
OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG = 3,
OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG = 4,
OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL = 5,
OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL
= 17,
OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED
= 18,
OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20,
OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21
};
enum open_args_open_claim4 {
OPEN_ARGS_OPEN_CLAIM_NULL = 0,
OPEN_ARGS_OPEN_CLAIM_PREVIOUS = 1,
OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR = 2,
OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
OPEN_ARGS_OPEN_CLAIM_FH = 4,
OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5,
OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6
};
enum open_args_createmode4 {
OPEN_ARGS_CREATEMODE_UNCHECKED4 = 0,
OPEN_ARGS_CREATE_MODE_GUARDED = 1,
OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2,
OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3
};
typedef open_arguments4 fattr4_open_arguments;
pragma public fattr4_open_arguments;
%/*
% * Determine what OPEN supports.
% */
const FATTR4_OPEN_ARGUMENTS = 86;
const OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010;
/*
* attributes for the delegation times being
* cached and served by the "client"
*/
typedef nfstime4 fattr4_time_deleg_access;
typedef nfstime4 fattr4_time_deleg_modify;
pragma public fattr4_time_deleg_access;
pragma public fattr4_time_deleg_modify;
%/*
% * New RECOMMENDED Attribute for
% * delegation caching of times
% */
const FATTR4_TIME_DELEG_ACCESS = 84;
const FATTR4_TIME_DELEG_MODIFY = 85;
/* new flags for share_access field of OPEN4args */
const OPEN4_SHARE_ACCESS_WANT_DELEG_MASK = 0xFF00;
const OPEN4_SHARE_ACCESS_WANT_NO_PREFERENCE = 0x0000;
const OPEN4_SHARE_ACCESS_WANT_READ_DELEG = 0x0100;
const OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG = 0x0200;
const OPEN4_SHARE_ACCESS_WANT_ANY_DELEG = 0x0300;
const OPEN4_SHARE_ACCESS_WANT_NO_DELEG = 0x0400;
const OPEN4_SHARE_ACCESS_WANT_CANCEL = 0x0500;
const OPEN4_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 0x10000;
const OPEN4_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 0x20000;
const OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000;
const OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000;
enum open_delegation_type4 {
OPEN_DELEGATE_NONE = 0,
OPEN_DELEGATE_READ = 1,
OPEN_DELEGATE_WRITE = 2,
OPEN_DELEGATE_NONE_EXT = 3, /* new to v4.1 */
OPEN_DELEGATE_READ_ATTRS_DELEG = 4,
OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5
};

@ -70,9 +70,6 @@ static unsigned long nlm_grace_period;
unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
static int nlm_udpport, nlm_tcpport;
/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
static unsigned int nlm_max_connections = 1024;
/*
* Constants needed for the sysctl interface.
*/
@ -136,9 +133,6 @@ lockd(void *vrqstp)
* NFS mount or NFS daemon has gone away.
*/
while (!svc_thread_should_stop(rqstp)) {
/* update sv_maxconn if it has changed */
rqstp->rq_server->sv_maxconn = nlm_max_connections;
nlmsvc_retry_blocked(rqstp);
svc_recv(rqstp);
}
@ -340,7 +334,6 @@ static int lockd_get(void)
return -ENOMEM;
}
serv->sv_maxconn = nlm_max_connections;
error = svc_set_num_threads(serv, NULL, 1);
if (error < 0) {
svc_destroy(&serv);
@ -542,7 +535,6 @@ module_param_call(nlm_udpport, param_set_port, param_get_int,
module_param_call(nlm_tcpport, param_set_port, param_get_int,
&nlm_tcpport, 0644);
module_param(nsm_use_hostnames, bool, 0644);
module_param(nlm_max_connections, uint, 0644);
static int lockd_init_net(struct net *net)
{

@ -211,10 +211,6 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
return ERR_PTR(-ENOMEM);
}
cb_info->serv = serv;
/* As there is only one thread we need to over-ride the
* default maximum of 80 connections
*/
serv->sv_maxconn = 1024;
dprintk("nfs_callback_create_svc: service created\n");
return serv;
}

@ -984,6 +984,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)
nfs_put_client(cps.clp);
goto out_invalidcred;
}
svc_xprt_set_valid(rqstp->rq_xprt);
}
cps.minorversion = hdr_arg.minorversion;

@ -18,9 +18,23 @@ nfsd-$(CONFIG_NFSD_V2) += nfsproc.o nfsxdr.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
nfs4acl.o nfs4callback.o nfs4recover.o
nfs4acl.o nfs4callback.o nfs4recover.o nfs4xdr_gen.o
nfsd-$(CONFIG_NFSD_PNFS) += nfs4layouts.o
nfsd-$(CONFIG_NFSD_BLOCKLAYOUT) += blocklayout.o blocklayoutxdr.o
nfsd-$(CONFIG_NFSD_SCSILAYOUT) += blocklayout.o blocklayoutxdr.o
nfsd-$(CONFIG_NFSD_FLEXFILELAYOUT) += flexfilelayout.o flexfilelayoutxdr.o
nfsd-$(CONFIG_NFS_LOCALIO) += localio.o
.PHONY: xdrgen
xdrgen: ../../include/linux/sunrpc/xdrgen/nfs4_1.h nfs4xdr_gen.h nfs4xdr_gen.c
../../include/linux/sunrpc/xdrgen/nfs4_1.h: ../../Documentation/sunrpc/xdr/nfs4_1.x
../../tools/net/sunrpc/xdrgen/xdrgen definitions $< > $@
nfs4xdr_gen.h: ../../Documentation/sunrpc/xdr/nfs4_1.x
../../tools/net/sunrpc/xdrgen/xdrgen declarations $< > $@
nfs4xdr_gen.c: ../../Documentation/sunrpc/xdr/nfs4_1.x
../../tools/net/sunrpc/xdrgen/xdrgen source $< > $@

@ -355,16 +355,25 @@ static void export_stats_destroy(struct export_stats *stats)
EXP_STATS_COUNTERS_NUM);
}
static void svc_export_put(struct kref *ref)
static void svc_export_release(struct rcu_head *rcu_head)
{
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
path_put(&exp->ex_path);
auth_domain_put(exp->ex_client);
struct svc_export *exp = container_of(rcu_head, struct svc_export,
ex_rcu);
nfsd4_fslocs_free(&exp->ex_fslocs);
export_stats_destroy(exp->ex_stats);
kfree(exp->ex_stats);
kfree(exp->ex_uuid);
kfree_rcu(exp, ex_rcu);
kfree(exp);
}
static void svc_export_put(struct kref *ref)
{
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
path_put(&exp->ex_path);
auth_domain_put(exp->ex_client);
call_rcu(&exp->ex_rcu, svc_export_release);
}
static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
@ -1425,13 +1434,9 @@ static int e_show(struct seq_file *m, void *p)
return 0;
}
if (!cache_get_rcu(&exp->h))
if (cache_check_rcu(cd, &exp->h, NULL))
return 0;
if (cache_check(cd, &exp->h, NULL))
return 0;
exp_put(exp);
return svc_export_show(m, cd, cp);
}

@ -128,12 +128,6 @@ struct nfsd_net {
seqlock_t writeverf_lock;
unsigned char writeverf[8];
/*
* Max number of connections this nfsd container will allow. Defaults
* to '0' which is means that it bases this on the number of threads.
*/
unsigned int max_connections;
u32 clientid_base;
u32 clientid_counter;
u32 clverifier_counter;

@ -42,6 +42,7 @@
#include "trace.h"
#include "xdr4cb.h"
#include "xdr4.h"
#include "nfs4xdr_gen.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
@ -93,12 +94,35 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
{
fattr->ncf_cb_change = 0;
fattr->ncf_cb_fsize = 0;
fattr->ncf_cb_atime.tv_sec = 0;
fattr->ncf_cb_atime.tv_nsec = 0;
fattr->ncf_cb_mtime.tv_sec = 0;
fattr->ncf_cb_mtime.tv_nsec = 0;
if (bitmap[0] & FATTR4_WORD0_CHANGE)
if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_change) < 0)
return -NFSERR_BAD_XDR;
if (bitmap[0] & FATTR4_WORD0_SIZE)
if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_fsize) < 0)
return -NFSERR_BAD_XDR;
if (bitmap[2] & FATTR4_WORD2_TIME_DELEG_ACCESS) {
fattr4_time_deleg_access access;
if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access))
return -NFSERR_BAD_XDR;
fattr->ncf_cb_atime.tv_sec = access.seconds;
fattr->ncf_cb_atime.tv_nsec = access.nseconds;
}
if (bitmap[2] & FATTR4_WORD2_TIME_DELEG_MODIFY) {
fattr4_time_deleg_modify modify;
if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify))
return -NFSERR_BAD_XDR;
fattr->ncf_cb_mtime.tv_sec = modify.seconds;
fattr->ncf_cb_mtime.tv_nsec = modify.nseconds;
}
return 0;
}
@ -361,16 +385,24 @@ static void
encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr,
struct nfs4_cb_fattr *fattr)
{
struct nfs4_delegation *dp =
container_of(fattr, struct nfs4_delegation, dl_cb_fattr);
struct nfs4_delegation *dp = container_of(fattr, struct nfs4_delegation, dl_cb_fattr);
struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle;
u32 bmap[1];
struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
u32 bmap_size = 1;
u32 bmap[3];
bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE;
bmap[0] = FATTR4_WORD0_SIZE;
if (!ncf->ncf_file_modified)
bmap[0] |= FATTR4_WORD0_CHANGE;
if (deleg_attrs_deleg(dp->dl_type)) {
bmap[1] = 0;
bmap[2] = FATTR4_WORD2_TIME_DELEG_ACCESS | FATTR4_WORD2_TIME_DELEG_MODIFY;
bmap_size = 3;
}
encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR);
encode_nfs_fh4(xdr, fh);
encode_bitmap4(xdr, bmap, ARRAY_SIZE(bmap));
encode_bitmap4(xdr, bmap, bmap_size);
hdr->nops++;
}
@ -634,7 +666,7 @@ static int nfs4_xdr_dec_cb_getattr(struct rpc_rqst *rqstp,
struct nfs4_cb_compound_hdr hdr;
int status;
u32 bitmap[3] = {0};
u32 attrlen;
u32 attrlen, maxlen;
struct nfs4_cb_fattr *ncf =
container_of(cb, struct nfs4_cb_fattr, ncf_getattr);
@ -647,13 +679,17 @@ static int nfs4_xdr_dec_cb_getattr(struct rpc_rqst *rqstp,
return status;
status = decode_cb_op_status(xdr, OP_CB_GETATTR, &cb->cb_status);
if (status)
if (unlikely(status || cb->cb_seq_status))
return status;
if (xdr_stream_decode_uint32_array(xdr, bitmap, 3) < 0)
return -NFSERR_BAD_XDR;
if (xdr_stream_decode_u32(xdr, &attrlen) < 0)
return -NFSERR_BAD_XDR;
if (attrlen > (sizeof(ncf->ncf_cb_change) + sizeof(ncf->ncf_cb_fsize)))
maxlen = sizeof(ncf->ncf_cb_change) + sizeof(ncf->ncf_cb_fsize);
if (bitmap[2] != 0)
maxlen += (sizeof(ncf->ncf_cb_mtime.tv_sec) +
sizeof(ncf->ncf_cb_mtime.tv_nsec)) * 2;
if (attrlen > maxlen)
return -NFSERR_BAD_XDR;
status = decode_cb_fattr4(xdr, bitmap, ncf);
return status;
@ -1036,8 +1072,7 @@ static void nfsd41_cb_inflight_begin(struct nfs4_client *clp)
static void nfsd41_cb_inflight_end(struct nfs4_client *clp)
{
if (atomic_dec_and_test(&clp->cl_cb_inflight))
wake_up_var(&clp->cl_cb_inflight);
atomic_dec_and_wake_up(&clp->cl_cb_inflight);
}
static void nfsd41_cb_inflight_wait_complete(struct nfs4_client *clp)
@ -1397,8 +1432,9 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
return;
if (cb->cb_status) {
WARN_ONCE(task->tk_status, "cb_status=%d tk_status=%d",
cb->cb_status, task->tk_status);
WARN_ONCE(task->tk_status,
"cb_status=%d tk_status=%d cb_opcode=%d",
cb->cb_status, task->tk_status, cb->cb_ops->opcode);
task->tk_status = cb->cb_status;
}

@ -1135,18 +1135,43 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
.na_iattr = &setattr->sa_iattr,
.na_seclabel = &setattr->sa_label,
};
bool save_no_wcc, deleg_attrs;
struct nfs4_stid *st = NULL;
struct inode *inode;
__be32 status = nfs_ok;
bool save_no_wcc;
int err;
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
deleg_attrs = setattr->sa_bmval[2] & (FATTR4_WORD2_TIME_DELEG_ACCESS |
FATTR4_WORD2_TIME_DELEG_MODIFY);
if (deleg_attrs || (setattr->sa_iattr.ia_valid & ATTR_SIZE)) {
int flags = WR_STATE;
if (setattr->sa_bmval[2] & FATTR4_WORD2_TIME_DELEG_ACCESS)
flags |= RD_STATE;
status = nfs4_preprocess_stateid_op(rqstp, cstate,
&cstate->current_fh, &setattr->sa_stateid,
WR_STATE, NULL, NULL);
flags, NULL, &st);
if (status)
return status;
}
if (deleg_attrs) {
status = nfserr_bad_stateid;
if (st->sc_type & SC_TYPE_DELEG) {
struct nfs4_delegation *dp = delegstateid(st);
/* Only for *_ATTRS_DELEG flavors */
if (deleg_attrs_deleg(dp->dl_type))
status = nfs_ok;
}
}
if (st)
nfs4_put_stid(st);
if (status)
return status;
err = fh_want_write(&cstate->current_fh);
if (err)
return nfserrno(err);

@ -2051,7 +2051,6 @@ static inline int check_for_legacy_methods(int status, struct net *net)
path_put(&path);
if (status)
return -ENOTDIR;
status = nn->client_tracking_ops->init(net);
}
return status;
}

@ -1909,15 +1909,84 @@ gen_sessionid(struct nfsd4_session *ses)
*/
#define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44)
static struct shrinker *nfsd_slot_shrinker;
static DEFINE_SPINLOCK(nfsd_session_list_lock);
static LIST_HEAD(nfsd_session_list);
/* The sum of "target_slots-1" on every session. The shrinker can push this
* down, though it can take a little while for the memory to actually
* be freed. The "-1" is because we can never free slot 0 while the
* session is active.
*/
static atomic_t nfsd_total_target_slots = ATOMIC_INIT(0);
static void
free_session_slots(struct nfsd4_session *ses)
free_session_slots(struct nfsd4_session *ses, int from)
{
int i;
for (i = 0; i < ses->se_fchannel.maxreqs; i++) {
free_svc_cred(&ses->se_slots[i]->sl_cred);
kfree(ses->se_slots[i]);
if (from >= ses->se_fchannel.maxreqs)
return;
for (i = from; i < ses->se_fchannel.maxreqs; i++) {
struct nfsd4_slot *slot = xa_load(&ses->se_slots, i);
/*
* Save the seqid in case we reactivate this slot.
* This will never require a memory allocation so GFP
* flag is irrelevant
*/
xa_store(&ses->se_slots, i, xa_mk_value(slot->sl_seqid), 0);
free_svc_cred(&slot->sl_cred);
kfree(slot);
}
ses->se_fchannel.maxreqs = from;
if (ses->se_target_maxslots > from) {
int new_target = from ?: 1;
atomic_sub(ses->se_target_maxslots - new_target, &nfsd_total_target_slots);
ses->se_target_maxslots = new_target;
}
}
/**
* reduce_session_slots - reduce the target max-slots of a session if possible
* @ses: The session to affect
* @dec: how much to decrease the target by
*
* This interface can be used by a shrinker to reduce the target max-slots
* for a session so that some slots can eventually be freed.
* It uses spin_trylock() as it may be called in a context where another
* spinlock is held that has a dependency on client_lock. As shrinkers are
* best-effort, skiping a session is client_lock is already held has no
* great coast
*
* Return value:
* The number of slots that the target was reduced by.
*/
static int
reduce_session_slots(struct nfsd4_session *ses, int dec)
{
struct nfsd_net *nn = net_generic(ses->se_client->net,
nfsd_net_id);
int ret = 0;
if (ses->se_target_maxslots <= 1)
return ret;
if (!spin_trylock(&nn->client_lock))
return ret;
ret = min(dec, ses->se_target_maxslots-1);
ses->se_target_maxslots -= ret;
atomic_sub(ret, &nfsd_total_target_slots);
ses->se_slot_gen += 1;
if (ses->se_slot_gen == 0) {
int i;
ses->se_slot_gen = 1;
for (i = 0; i < ses->se_fchannel.maxreqs; i++) {
struct nfsd4_slot *slot = xa_load(&ses->se_slots, i);
slot->sl_generation = 0;
}
}
spin_unlock(&nn->client_lock);
return ret;
}
/*
@ -1935,89 +2004,46 @@ static inline u32 slot_bytes(struct nfsd4_channel_attrs *ca)
return size + sizeof(struct nfsd4_slot);
}
/*
* XXX: If we run out of reserved DRC memory we could (up to a point)
* re-negotiate active sessions and reduce their slot usage to make
* room for new connections. For now we just fail the create session.
*/
static u32 nfsd4_get_drc_mem(struct nfsd4_channel_attrs *ca, struct nfsd_net *nn)
{
u32 slotsize = slot_bytes(ca);
u32 num = ca->maxreqs;
unsigned long avail, total_avail;
unsigned int scale_factor;
spin_lock(&nfsd_drc_lock);
if (nfsd_drc_max_mem > nfsd_drc_mem_used)
total_avail = nfsd_drc_max_mem - nfsd_drc_mem_used;
else
/* We have handed out more space than we chose in
* set_max_drc() to allow. That isn't really a
* problem as long as that doesn't make us think we
* have lots more due to integer overflow.
*/
total_avail = 0;
avail = min((unsigned long)NFSD_MAX_MEM_PER_SESSION, total_avail);
/*
* Never use more than a fraction of the remaining memory,
* unless it's the only way to give this client a slot.
* The chosen fraction is either 1/8 or 1/number of threads,
* whichever is smaller. This ensures there are adequate
* slots to support multiple clients per thread.
* Give the client one slot even if that would require
* over-allocation--it is better than failure.
*/
scale_factor = max_t(unsigned int, 8, nn->nfsd_serv->sv_nrthreads);
avail = clamp_t(unsigned long, avail, slotsize,
total_avail/scale_factor);
num = min_t(int, num, avail / slotsize);
num = max_t(int, num, 1);
nfsd_drc_mem_used += num * slotsize;
spin_unlock(&nfsd_drc_lock);
return num;
}
static void nfsd4_put_drc_mem(struct nfsd4_channel_attrs *ca)
{
int slotsize = slot_bytes(ca);
spin_lock(&nfsd_drc_lock);
nfsd_drc_mem_used -= slotsize * ca->maxreqs;
spin_unlock(&nfsd_drc_lock);
}
static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs,
struct nfsd4_channel_attrs *battrs)
{
int numslots = fattrs->maxreqs;
int slotsize = slot_bytes(fattrs);
struct nfsd4_session *new;
struct nfsd4_slot *slot;
int i;
BUILD_BUG_ON(struct_size(new, se_slots, NFSD_MAX_SLOTS_PER_SESSION)
> PAGE_SIZE);
new = kzalloc(struct_size(new, se_slots, numslots), GFP_KERNEL);
new = kzalloc(sizeof(*new), GFP_KERNEL);
if (!new)
return NULL;
xa_init(&new->se_slots);
/* allocate each struct nfsd4_slot and data cache in one piece */
for (i = 0; i < numslots; i++) {
new->se_slots[i] = kzalloc(slotsize, GFP_KERNEL);
if (!new->se_slots[i])
goto out_free;
}
slot = kzalloc(slotsize, GFP_KERNEL);
if (!slot || xa_is_err(xa_store(&new->se_slots, 0, slot, GFP_KERNEL)))
goto out_free;
for (i = 1; i < numslots; i++) {
const gfp_t gfp = GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN;
slot = kzalloc(slotsize, gfp);
if (!slot)
break;
if (xa_is_err(xa_store(&new->se_slots, i, slot, gfp))) {
kfree(slot);
break;
}
}
fattrs->maxreqs = i;
memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs));
new->se_target_maxslots = i;
atomic_add(i - 1, &nfsd_total_target_slots);
new->se_cb_slot_avail = ~0U;
new->se_cb_highest_slot = min(battrs->maxreqs - 1,
NFSD_BC_SLOT_TABLE_SIZE - 1);
spin_lock_init(&new->se_lock);
return new;
out_free:
while (i--)
kfree(new->se_slots[i]);
kfree(slot);
xa_destroy(&new->se_slots);
kfree(new);
return NULL;
}
@ -2123,17 +2149,47 @@ static void nfsd4_del_conns(struct nfsd4_session *s)
static void __free_session(struct nfsd4_session *ses)
{
free_session_slots(ses);
free_session_slots(ses, 0);
xa_destroy(&ses->se_slots);
kfree(ses);
}
static void free_session(struct nfsd4_session *ses)
{
nfsd4_del_conns(ses);
nfsd4_put_drc_mem(&ses->se_fchannel);
__free_session(ses);
}
static unsigned long
nfsd_slot_count(struct shrinker *s, struct shrink_control *sc)
{
unsigned long cnt = atomic_read(&nfsd_total_target_slots);
return cnt ? cnt : SHRINK_EMPTY;
}
static unsigned long
nfsd_slot_scan(struct shrinker *s, struct shrink_control *sc)
{
struct nfsd4_session *ses;
unsigned long scanned = 0;
unsigned long freed = 0;
spin_lock(&nfsd_session_list_lock);
list_for_each_entry(ses, &nfsd_session_list, se_all_sessions) {
freed += reduce_session_slots(ses, 1);
scanned += 1;
if (scanned >= sc->nr_to_scan) {
/* Move starting point for next scan */
list_move(&nfsd_session_list, &ses->se_all_sessions);
break;
}
}
spin_unlock(&nfsd_session_list_lock);
sc->nr_scanned = scanned;
return freed;
}
static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses)
{
int idx;
@ -2158,6 +2214,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&clp->cl_lock);
spin_lock(&nfsd_session_list_lock);
list_add_tail(&new->se_all_sessions, &nfsd_session_list);
spin_unlock(&nfsd_session_list_lock);
{
struct sockaddr *sa = svc_addr(rqstp);
/*
@ -2227,6 +2287,9 @@ unhash_session(struct nfsd4_session *ses)
spin_lock(&ses->se_client->cl_lock);
list_del(&ses->se_perclnt);
spin_unlock(&ses->se_client->cl_lock);
spin_lock(&nfsd_session_list_lock);
list_del(&ses->se_all_sessions);
spin_unlock(&nfsd_session_list_lock);
}
/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
@ -2362,8 +2425,12 @@ unhash_client_locked(struct nfs4_client *clp)
}
list_del_init(&clp->cl_lru);
spin_lock(&clp->cl_lock);
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
spin_lock(&nfsd_session_list_lock);
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) {
list_del_init(&ses->se_hash);
list_del_init(&ses->se_all_sessions);
}
spin_unlock(&nfsd_session_list_lock);
spin_unlock(&clp->cl_lock);
}
@ -2685,6 +2752,7 @@ static const char *cb_state2str(int state)
static int client_info_show(struct seq_file *m, void *v)
{
struct inode *inode = file_inode(m->file);
struct nfsd4_session *ses;
struct nfs4_client *clp;
u64 clid;
@ -2721,6 +2789,16 @@ static int client_info_show(struct seq_file *m, void *v)
seq_printf(m, "callback address: \"%pISpc\"\n", &clp->cl_cb_conn.cb_addr);
seq_printf(m, "admin-revoked states: %d\n",
atomic_read(&clp->cl_admin_revoked));
spin_lock(&clp->cl_lock);
seq_printf(m, "session slots:");
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
seq_printf(m, " %u", ses->se_fchannel.maxreqs);
seq_printf(m, "\nsession target slots:");
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
seq_printf(m, " %u", ses->se_target_maxslots);
spin_unlock(&clp->cl_lock);
seq_puts(m, "\n");
drop_client(clp);
return 0;
@ -2873,6 +2951,21 @@ static int nfs4_show_lock(struct seq_file *s, struct nfs4_stid *st)
return 0;
}
static char *nfs4_show_deleg_type(u32 dl_type)
{
switch (dl_type) {
case OPEN_DELEGATE_READ:
return "r";
case OPEN_DELEGATE_WRITE:
return "w";
case OPEN_DELEGATE_READ_ATTRS_DELEG:
return "ra";
case OPEN_DELEGATE_WRITE_ATTRS_DELEG:
return "wa";
}
return "?";
}
static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
{
struct nfs4_delegation *ds;
@ -2886,8 +2979,7 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
nfs4_show_stateid(s, &st->sc_stateid);
seq_puts(s, ": { type: deleg, ");
seq_printf(s, "access: %s",
ds->dl_type == NFS4_OPEN_DELEGATE_READ ? "r" : "w");
seq_printf(s, "access: %s", nfs4_show_deleg_type(ds->dl_type));
/* XXX: lease time, whether it's being recalled. */
@ -3708,10 +3800,10 @@ nfsd4_exchange_id_release(union nfsd4_op_u *u)
kfree(exid->server_impl_name);
}
static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse)
static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, u8 flags)
{
/* The slot is in use, and no response has been sent. */
if (slot_inuse) {
if (flags & NFSD4_SLOT_INUSE) {
if (seqid == slot_seqid)
return nfserr_jukebox;
else
@ -3720,6 +3812,8 @@ static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse)
/* Note unsigned 32-bit arithmetic handles wraparound: */
if (likely(seqid == slot_seqid + 1))
return nfs_ok;
if ((flags & NFSD4_SLOT_REUSED) && seqid == 1)
return nfs_ok;
if (seqid == slot_seqid)
return nfserr_replay_cache;
return nfserr_seq_misordered;
@ -3778,17 +3872,6 @@ static __be32 check_forechannel_attrs(struct nfsd4_channel_attrs *ca, struct nfs
ca->maxresp_cached = min_t(u32, ca->maxresp_cached,
NFSD_SLOT_CACHE_SIZE + NFSD_MIN_HDR_SEQ_SZ);
ca->maxreqs = min_t(u32, ca->maxreqs, NFSD_MAX_SLOTS_PER_SESSION);
/*
* Note decreasing slot size below client's request may make it
* difficult for client to function correctly, whereas
* decreasing the number of slots will (just?) affect
* performance. When short on memory we therefore prefer to
* decrease number of slots instead of their size. Clients that
* request larger slots than they need will get poor results:
* Note that we always allow at least one slot, because our
* accounting is soft and provides no guarantees either way.
*/
ca->maxreqs = nfsd4_get_drc_mem(ca, nn);
return nfs_ok;
}
@ -3866,11 +3949,11 @@ nfsd4_create_session(struct svc_rqst *rqstp,
return status;
status = check_backchannel_attrs(&cr_ses->back_channel);
if (status)
goto out_release_drc_mem;
goto out_err;
status = nfserr_jukebox;
new = alloc_session(&cr_ses->fore_channel, &cr_ses->back_channel);
if (!new)
goto out_release_drc_mem;
goto out_err;
conn = alloc_conn_from_crses(rqstp, cr_ses);
if (!conn)
goto out_free_session;
@ -3979,8 +4062,7 @@ out_free_conn:
free_conn(conn);
out_free_session:
__free_session(new);
out_release_drc_mem:
nfsd4_put_drc_mem(&cr_ses->fore_channel);
out_err:
return status;
}
@ -4278,17 +4360,11 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (seq->slotid >= session->se_fchannel.maxreqs)
goto out_put_session;
slot = session->se_slots[seq->slotid];
slot = xa_load(&session->se_slots, seq->slotid);
dprintk("%s: slotid %d\n", __func__, seq->slotid);
/* We do not negotiate the number of slots yet, so set the
* maxslots to the session maxreqs which is used to encode
* sr_highest_slotid and the sr_target_slot id to maxslots */
seq->maxslots = session->se_fchannel.maxreqs;
trace_nfsd_slot_seqid_sequence(clp, seq, slot);
status = check_slot_seqid(seq->seqid, slot->sl_seqid,
slot->sl_flags & NFSD4_SLOT_INUSE);
status = check_slot_seqid(seq->seqid, slot->sl_seqid, slot->sl_flags);
if (status == nfserr_replay_cache) {
status = nfserr_seq_misordered;
if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED))
@ -4313,6 +4389,12 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
goto out_put_session;
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
seq->maxslots <= session->se_target_maxslots)
/* Client acknowledged our reduce maxreqs */
free_session_slots(session, session->se_target_maxslots);
buflen = (seq->cachethis) ?
session->se_fchannel.maxresp_cached :
session->se_fchannel.maxresp_sz;
@ -4323,9 +4405,11 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
svc_reserve(rqstp, buflen);
status = nfs_ok;
/* Success! bump slot seqid */
/* Success! accept new slot seqid */
slot->sl_seqid = seq->seqid;
slot->sl_flags &= ~NFSD4_SLOT_REUSED;
slot->sl_flags |= NFSD4_SLOT_INUSE;
slot->sl_generation = session->se_slot_gen;
if (seq->cachethis)
slot->sl_flags |= NFSD4_SLOT_CACHETHIS;
else
@ -4335,6 +4419,49 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
cstate->session = session;
cstate->clp = clp;
/*
* If the client ever uses the highest available slot,
* gently try to allocate another 20%. This allows
* fairly quick growth without grossly over-shooting what
* the client might use.
*/
if (seq->slotid == session->se_fchannel.maxreqs - 1 &&
session->se_target_maxslots >= session->se_fchannel.maxreqs &&
session->se_fchannel.maxreqs < NFSD_MAX_SLOTS_PER_SESSION) {
int s = session->se_fchannel.maxreqs;
int cnt = DIV_ROUND_UP(s, 5);
void *prev_slot;
do {
/*
* GFP_NOWAIT both allows allocation under a
* spinlock, and only succeeds if there is
* plenty of memory.
*/
slot = kzalloc(slot_bytes(&session->se_fchannel),
GFP_NOWAIT);
prev_slot = xa_load(&session->se_slots, s);
if (xa_is_value(prev_slot) && slot) {
slot->sl_seqid = xa_to_value(prev_slot);
slot->sl_flags |= NFSD4_SLOT_REUSED;
}
if (slot &&
!xa_is_err(xa_store(&session->se_slots, s, slot,
GFP_NOWAIT))) {
s += 1;
session->se_fchannel.maxreqs = s;
atomic_add(s - session->se_target_maxslots,
&nfsd_total_target_slots);
session->se_target_maxslots = s;
} else {
kfree(slot);
slot = NULL;
}
} while (slot && --cnt > 0);
}
seq->maxslots = max(session->se_target_maxslots, seq->maxslots);
seq->target_maxslots = session->se_target_maxslots;
out:
switch (clp->cl_cb_state) {
case NFSD4_CB_DOWN:
@ -4739,7 +4866,7 @@ static void init_nfs4_replay(struct nfs4_replay *rp)
rp->rp_status = nfserr_serverfault;
rp->rp_buflen = 0;
rp->rp_buf = rp->rp_ibuf;
atomic_set(&rp->rp_locked, RP_UNLOCKED);
rp->rp_locked = RP_UNLOCKED;
}
static int nfsd4_cstate_assign_replay(struct nfsd4_compound_state *cstate,
@ -4747,9 +4874,9 @@ static int nfsd4_cstate_assign_replay(struct nfsd4_compound_state *cstate,
{
if (!nfsd4_has_session(cstate)) {
wait_var_event(&so->so_replay.rp_locked,
atomic_cmpxchg(&so->so_replay.rp_locked,
RP_UNLOCKED, RP_LOCKED) != RP_LOCKED);
if (atomic_read(&so->so_replay.rp_locked) == RP_UNHASHED)
cmpxchg(&so->so_replay.rp_locked,
RP_UNLOCKED, RP_LOCKED) != RP_LOCKED);
if (so->so_replay.rp_locked == RP_UNHASHED)
return -EAGAIN;
cstate->replay_owner = nfs4_get_stateowner(so);
}
@ -4762,9 +4889,7 @@ void nfsd4_cstate_clear_replay(struct nfsd4_compound_state *cstate)
if (so != NULL) {
cstate->replay_owner = NULL;
atomic_set(&so->so_replay.rp_locked, RP_UNLOCKED);
smp_mb__after_atomic();
wake_up_var(&so->so_replay.rp_locked);
store_release_wake_up(&so->so_replay.rp_locked, RP_UNLOCKED);
nfs4_put_stateowner(so);
}
}
@ -5069,9 +5194,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
* Some threads with a reference might be waiting for rp_locked,
* so tell them to stop waiting.
*/
atomic_set(&oo->oo_owner.so_replay.rp_locked, RP_UNHASHED);
smp_mb__after_atomic();
wake_up_var(&oo->oo_owner.so_replay.rp_locked);
store_release_wake_up(&oo->oo_owner.so_replay.rp_locked, RP_UNHASHED);
wait_event(close_wq, refcount_read(&s->st_stid.sc_count) == 2);
release_all_access(s);
@ -5472,7 +5595,7 @@ retry:
static inline __be32
nfs4_check_delegmode(struct nfs4_delegation *dp, int flags)
{
if ((flags & WR_STATE) && (dp->dl_type == NFS4_OPEN_DELEGATE_READ))
if (!(flags & RD_STATE) && deleg_is_read(dp->dl_type))
return nfserr_openmode;
else
return nfs_ok;
@ -5704,8 +5827,7 @@ static bool nfsd4_cb_channel_good(struct nfs4_client *clp)
return clp->cl_minorversion && clp->cl_cb_state == NFSD4_CB_UNKNOWN;
}
static struct file_lease *nfs4_alloc_init_lease(struct nfs4_delegation *dp,
int flag)
static struct file_lease *nfs4_alloc_init_lease(struct nfs4_delegation *dp)
{
struct file_lease *fl;
@ -5714,7 +5836,7 @@ static struct file_lease *nfs4_alloc_init_lease(struct nfs4_delegation *dp,
return NULL;
fl->fl_lmops = &nfsd_lease_mng_ops;
fl->c.flc_flags = FL_DELEG;
fl->c.flc_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
fl->c.flc_type = deleg_is_read(dp->dl_type) ? F_RDLCK : F_WRLCK;
fl->c.flc_owner = (fl_owner_t)dp;
fl->c.flc_pid = current->tgid;
fl->c.flc_file = dp->dl_stid.sc_file->fi_deleg_file->nf_file;
@ -5829,13 +5951,14 @@ static struct nfs4_delegation *
nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
struct svc_fh *parent)
{
int status = 0;
bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
struct nfs4_client *clp = stp->st_stid.sc_client;
struct nfs4_file *fp = stp->st_stid.sc_file;
struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate;
struct nfs4_delegation *dp;
struct nfsd_file *nf = NULL;
struct file_lease *fl;
int status = 0;
u32 dl_type;
/*
@ -5860,7 +5983,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
*/
if ((open->op_share_access & NFS4_SHARE_ACCESS_BOTH) == NFS4_SHARE_ACCESS_BOTH) {
nf = find_rw_file(fp);
dl_type = NFS4_OPEN_DELEGATE_WRITE;
dl_type = deleg_ts ? OPEN_DELEGATE_WRITE_ATTRS_DELEG : OPEN_DELEGATE_WRITE;
}
/*
@ -5869,7 +5992,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
*/
if (!nf && (open->op_share_access & NFS4_SHARE_ACCESS_READ)) {
nf = find_readable_file(fp);
dl_type = NFS4_OPEN_DELEGATE_READ;
dl_type = deleg_ts ? OPEN_DELEGATE_READ_ATTRS_DELEG : OPEN_DELEGATE_READ;
}
if (!nf)
@ -5901,7 +6024,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
if (!dp)
goto out_delegees;
fl = nfs4_alloc_init_lease(dp, dl_type);
fl = nfs4_alloc_init_lease(dp);
if (!fl)
goto out_clnt_odstate;
@ -5958,20 +6081,20 @@ out_delegees:
static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
{
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
open->op_delegate_type = OPEN_DELEGATE_NONE_EXT;
if (status == -EAGAIN)
open->op_why_no_deleg = WND4_CONTENTION;
else {
open->op_why_no_deleg = WND4_RESOURCE;
switch (open->op_deleg_want) {
case NFS4_SHARE_WANT_READ_DELEG:
case NFS4_SHARE_WANT_WRITE_DELEG:
case NFS4_SHARE_WANT_ANY_DELEG:
case OPEN4_SHARE_ACCESS_WANT_READ_DELEG:
case OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG:
case OPEN4_SHARE_ACCESS_WANT_ANY_DELEG:
break;
case NFS4_SHARE_WANT_CANCEL:
case OPEN4_SHARE_ACCESS_WANT_CANCEL:
open->op_why_no_deleg = WND4_CANCELLED;
break;
case NFS4_SHARE_WANT_NO_DELEG:
case OPEN4_SHARE_ACCESS_WANT_NO_DELEG:
WARN_ON_ONCE(1);
}
}
@ -6027,13 +6150,14 @@ static void
nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
struct svc_fh *currentfh)
{
struct nfs4_delegation *dp;
bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
struct nfs4_openowner *oo = openowner(stp->st_stateowner);
struct nfs4_client *clp = stp->st_stid.sc_client;
struct svc_fh *parent = NULL;
int cb_up;
int status = 0;
struct nfs4_delegation *dp;
struct kstat stat;
int status = 0;
int cb_up;
cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client);
open->op_recall = false;
@ -6074,20 +6198,22 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
destroy_delegation(dp);
goto out_no_deleg;
}
open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE;
open->op_delegate_type = deleg_ts ? OPEN_DELEGATE_WRITE_ATTRS_DELEG :
OPEN_DELEGATE_WRITE;
dp->dl_cb_fattr.ncf_cur_fsize = stat.size;
dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat);
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
} else {
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
open->op_delegate_type = deleg_ts ? OPEN_DELEGATE_READ_ATTRS_DELEG :
OPEN_DELEGATE_READ;
trace_nfsd_deleg_read(&dp->dl_stid.sc_stateid);
}
nfs4_put_stid(&dp->dl_stid);
return;
out_no_deleg:
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
open->op_delegate_type = OPEN_DELEGATE_NONE;
if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) {
open->op_delegate_type != OPEN_DELEGATE_NONE) {
dprintk("NFSD: WARNING: refusing delegation reclaim\n");
open->op_recall = true;
}
@ -6101,21 +6227,32 @@ out_no_deleg:
static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open,
struct nfs4_delegation *dp)
{
if (open->op_deleg_want == NFS4_SHARE_WANT_READ_DELEG &&
dp->dl_type == NFS4_OPEN_DELEGATE_WRITE) {
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
open->op_why_no_deleg = WND4_NOT_SUPP_DOWNGRADE;
} else if (open->op_deleg_want == NFS4_SHARE_WANT_WRITE_DELEG &&
dp->dl_type == NFS4_OPEN_DELEGATE_WRITE) {
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
open->op_why_no_deleg = WND4_NOT_SUPP_UPGRADE;
if (deleg_is_write(dp->dl_type)) {
if (open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_READ_DELEG) {
open->op_delegate_type = OPEN_DELEGATE_NONE_EXT;
open->op_why_no_deleg = WND4_NOT_SUPP_DOWNGRADE;
} else if (open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG) {
open->op_delegate_type = OPEN_DELEGATE_NONE_EXT;
open->op_why_no_deleg = WND4_NOT_SUPP_UPGRADE;
}
}
/* Otherwise the client must be confused wanting a delegation
* it already has, therefore we don't return
* NFS4_OPEN_DELEGATE_NONE_EXT and reason.
* OPEN_DELEGATE_NONE_EXT and reason.
*/
}
/* Are we returning only a delegation stateid? */
static bool open_xor_delegation(struct nfsd4_open *open)
{
if (!(open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION))
return false;
/* Did we actually get a delegation? */
if (!deleg_is_read(open->op_delegate_type) && !deleg_is_write(open->op_delegate_type))
return false;
return true;
}
/**
* nfsd4_process_open2 - finish open processing
* @rqstp: the RPC transaction being executed
@ -6201,8 +6338,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
mutex_unlock(&stp->st_mutex);
if (nfsd4_has_session(&resp->cstate)) {
if (open->op_deleg_want & NFS4_SHARE_WANT_NO_DELEG) {
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
if (open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_NO_DELEG) {
open->op_delegate_type = OPEN_DELEGATE_NONE_EXT;
open->op_why_no_deleg = WND4_NOT_WANTED;
goto nodeleg;
}
@ -6213,12 +6350,23 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
* OPEN succeeds even if we fail.
*/
nfs4_open_delegation(open, stp, &resp->cstate.current_fh);
/*
* If there is an existing open stateid, it must be updated and
* returned. Only respect WANT_OPEN_XOR_DELEGATION when a new
* open stateid would have to be created.
*/
if (new_stp && open_xor_delegation(open)) {
memcpy(&open->op_stateid, &zero_stateid, sizeof(open->op_stateid));
open->op_rflags |= OPEN4_RESULT_NO_OPEN_STATEID;
release_open_stateid(stp);
}
nodeleg:
status = nfs_ok;
trace_nfsd_open(&stp->st_stid.sc_stateid);
out:
/* 4.1 client trying to upgrade/downgrade delegation? */
if (open->op_delegate_type == NFS4_OPEN_DELEGATE_NONE && dp &&
if (open->op_delegate_type == OPEN_DELEGATE_NONE && dp &&
open->op_deleg_want)
nfsd4_deleg_xgrade_none_ext(open, dp);
@ -6229,7 +6377,7 @@ out:
/*
* To finish the open response, we just need to set the rflags.
*/
open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX;
open->op_rflags |= NFS4_OPEN_RESULT_LOCKTYPE_POSIX;
if (nfsd4_has_session(&resp->cstate))
open->op_rflags |= NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK;
else if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED))
@ -7966,7 +8114,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_blocked_lock *nbl = NULL;
struct file_lock *file_lock = NULL;
struct file_lock *conflock = NULL;
struct super_block *sb;
__be32 status = 0;
int lkflg;
int err;
@ -7986,7 +8133,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0);
if (status != nfs_ok)
return status;
sb = cstate->current_fh.fh_dentry->d_sb;
if (lock->lk_is_new) {
if (nfsd4_has_session(cstate))
@ -8721,7 +8867,6 @@ skip_grace:
}
/* initialization to perform when the nfsd service is started: */
int
nfs4_state_start(void)
{
@ -8731,6 +8876,15 @@ nfs4_state_start(void)
if (ret)
return ret;
nfsd_slot_shrinker = shrinker_alloc(0, "nfsd-DRC-slot");
if (!nfsd_slot_shrinker) {
rhltable_destroy(&nfs4_file_rhltable);
return -ENOMEM;
}
nfsd_slot_shrinker->count_objects = nfsd_slot_count;
nfsd_slot_shrinker->scan_objects = nfsd_slot_scan;
shrinker_register(nfsd_slot_shrinker);
set_max_delegations();
return 0;
}
@ -8772,6 +8926,7 @@ void
nfs4_state_shutdown(void)
{
rhltable_destroy(&nfs4_file_rhltable);
shrinker_free(nfsd_slot_shrinker);
}
static void
@ -8888,6 +9043,78 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
get_stateid(cstate, &u->write.wr_stateid);
}
/**
* set_cb_time - vet and set the timespec for a cb_getattr update
* @cb: timestamp from the CB_GETATTR response
* @orig: original timestamp in the inode
* @now: current time
*
* Given a timestamp in a CB_GETATTR response, check it against the
* current timestamp in the inode and the current time. Returns true
* if the inode's timestamp needs to be updated, and false otherwise.
* @cb may also be changed if the timestamp needs to be clamped.
*/
static bool set_cb_time(struct timespec64 *cb, const struct timespec64 *orig,
const struct timespec64 *now)
{
/*
* "When the time presented is before the original time, then the
* update is ignored." Also no need to update if there is no change.
*/
if (timespec64_compare(cb, orig) <= 0)
return false;
/*
* "When the time presented is in the future, the server can either
* clamp the new time to the current time, or it may
* return NFS4ERR_DELAY to the client, allowing it to retry."
*/
if (timespec64_compare(cb, now) > 0) {
/* clamp it */
*cb = *now;
}
return true;
}
static int cb_getattr_update_times(struct dentry *dentry, struct nfs4_delegation *dp)
{
struct inode *inode = d_inode(dentry);
struct timespec64 now = current_time(inode);
struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
struct iattr attrs = { };
int ret;
if (deleg_attrs_deleg(dp->dl_type)) {
struct timespec64 atime = inode_get_atime(inode);
struct timespec64 mtime = inode_get_mtime(inode);
attrs.ia_atime = ncf->ncf_cb_atime;
attrs.ia_mtime = ncf->ncf_cb_mtime;
if (set_cb_time(&attrs.ia_atime, &atime, &now))
attrs.ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
if (set_cb_time(&attrs.ia_mtime, &mtime, &now)) {
attrs.ia_valid |= ATTR_CTIME | ATTR_MTIME | ATTR_MTIME_SET;
attrs.ia_ctime = attrs.ia_mtime;
}
} else {
attrs.ia_valid |= ATTR_MTIME | ATTR_CTIME;
attrs.ia_mtime = attrs.ia_ctime = now;
}
if (!attrs.ia_valid)
return 0;
attrs.ia_valid |= ATTR_DELEG;
inode_lock(inode);
ret = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
inode_unlock(inode);
return ret;
}
/**
* nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict
* @rqstp: RPC transaction context
@ -8914,7 +9141,6 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
struct file_lock_context *ctx;
struct nfs4_delegation *dp = NULL;
struct file_lease *fl;
struct iattr attrs;
struct nfs4_cb_fattr *ncf;
struct inode *inode = d_inode(dentry);
@ -8976,11 +9202,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
* not update the file's metadata with the client's
* modified size
*/
attrs.ia_mtime = attrs.ia_ctime = current_time(inode);
attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG;
inode_lock(inode);
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
inode_unlock(inode);
err = cb_getattr_update_times(dentry, dp);
if (err) {
status = nfserrno(err);
goto out_status;

@ -55,6 +55,7 @@
#include "netns.h"
#include "pnfs.h"
#include "filecache.h"
#include "nfs4xdr_gen.h"
#include "trace.h"
@ -520,6 +521,26 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
*umask = mask & S_IRWXUGO;
iattr->ia_valid |= ATTR_MODE;
}
if (bmval[2] & FATTR4_WORD2_TIME_DELEG_ACCESS) {
fattr4_time_deleg_access access;
if (!xdrgen_decode_fattr4_time_deleg_access(argp->xdr, &access))
return nfserr_bad_xdr;
iattr->ia_atime.tv_sec = access.seconds;
iattr->ia_atime.tv_nsec = access.nseconds;
iattr->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET | ATTR_DELEG;
}
if (bmval[2] & FATTR4_WORD2_TIME_DELEG_MODIFY) {
fattr4_time_deleg_modify modify;
if (!xdrgen_decode_fattr4_time_deleg_modify(argp->xdr, &modify))
return nfserr_bad_xdr;
iattr->ia_mtime.tv_sec = modify.seconds;
iattr->ia_mtime.tv_nsec = modify.nseconds;
iattr->ia_ctime.tv_sec = modify.seconds;
iattr->ia_ctime.tv_nsec = modify.seconds;
iattr->ia_valid |= ATTR_CTIME | ATTR_MTIME | ATTR_MTIME_SET | ATTR_DELEG;
}
/* request sanity: did attrlist4 contain the expected number of words? */
if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos)
@ -1066,13 +1087,13 @@ static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *sh
return nfs_ok;
if (!argp->minorversion)
return nfserr_bad_xdr;
switch (w & NFS4_SHARE_WANT_MASK) {
case NFS4_SHARE_WANT_NO_PREFERENCE:
case NFS4_SHARE_WANT_READ_DELEG:
case NFS4_SHARE_WANT_WRITE_DELEG:
case NFS4_SHARE_WANT_ANY_DELEG:
case NFS4_SHARE_WANT_NO_DELEG:
case NFS4_SHARE_WANT_CANCEL:
switch (w & NFS4_SHARE_WANT_TYPE_MASK) {
case OPEN4_SHARE_ACCESS_WANT_NO_PREFERENCE:
case OPEN4_SHARE_ACCESS_WANT_READ_DELEG:
case OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG:
case OPEN4_SHARE_ACCESS_WANT_ANY_DELEG:
case OPEN4_SHARE_ACCESS_WANT_NO_DELEG:
case OPEN4_SHARE_ACCESS_WANT_CANCEL:
break;
default:
return nfserr_bad_xdr;
@ -1884,7 +1905,8 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
return nfserr_bad_xdr;
seq->seqid = be32_to_cpup(p++);
seq->slotid = be32_to_cpup(p++);
seq->maxslots = be32_to_cpup(p++);
/* sa_highest_slotid counts from 0 but maxslots counts from 1 ... */
seq->maxslots = be32_to_cpup(p++) + 1;
seq->cachethis = be32_to_cpup(p);
seq->status_flags = 0;
@ -2919,6 +2941,7 @@ struct nfsd4_fattr_args {
struct kstat stat;
struct kstatfs statfs;
struct nfs4_acl *acl;
u64 change_attr;
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
struct lsm_context context;
#endif
@ -3017,7 +3040,6 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
const struct svc_export *exp = args->exp;
u64 c;
if (unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
u32 flush_time = convert_to_wallclock(exp->cd->flush_time);
@ -3028,9 +3050,7 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
return nfserr_resource;
return nfs_ok;
}
c = nfsd4_change_attribute(&args->stat);
return nfsd4_encode_changeid4(xdr, c);
return nfsd4_encode_changeid4(xdr, args->change_attr);
}
static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr,
@ -3387,6 +3407,56 @@ static __be32 nfsd4_encode_fattr4_xattr_support(struct xdr_stream *xdr,
return nfsd4_encode_bool(xdr, err == 0);
}
#define NFSD_OA_SHARE_ACCESS (BIT(OPEN_ARGS_SHARE_ACCESS_READ) | \
BIT(OPEN_ARGS_SHARE_ACCESS_WRITE) | \
BIT(OPEN_ARGS_SHARE_ACCESS_BOTH))
#define NFSD_OA_SHARE_DENY (BIT(OPEN_ARGS_SHARE_DENY_NONE) | \
BIT(OPEN_ARGS_SHARE_DENY_READ) | \
BIT(OPEN_ARGS_SHARE_DENY_WRITE) | \
BIT(OPEN_ARGS_SHARE_DENY_BOTH))
#define NFSD_OA_SHARE_ACCESS_WANT (BIT(OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG) | \
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG) | \
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL) | \
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS) | \
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION))
#define NFSD_OA_OPEN_CLAIM (BIT(OPEN_ARGS_OPEN_CLAIM_NULL) | \
BIT(OPEN_ARGS_OPEN_CLAIM_PREVIOUS) | \
BIT(OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR) | \
BIT(OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV)| \
BIT(OPEN_ARGS_OPEN_CLAIM_FH) | \
BIT(OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH) | \
BIT(OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH))
#define NFSD_OA_CREATE_MODE (BIT(OPEN_ARGS_CREATEMODE_UNCHECKED4) | \
BIT(OPEN_ARGS_CREATE_MODE_GUARDED) | \
BIT(OPEN_ARGS_CREATEMODE_EXCLUSIVE4) | \
BIT(OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1))
static uint32_t oa_share_access = NFSD_OA_SHARE_ACCESS;
static uint32_t oa_share_deny = NFSD_OA_SHARE_DENY;
static uint32_t oa_share_access_want = NFSD_OA_SHARE_ACCESS_WANT;
static uint32_t oa_open_claim = NFSD_OA_OPEN_CLAIM;
static uint32_t oa_create_mode = NFSD_OA_CREATE_MODE;
static const struct open_arguments4 nfsd_open_arguments = {
.oa_share_access = { .count = 1, .element = &oa_share_access },
.oa_share_deny = { .count = 1, .element = &oa_share_deny },
.oa_share_access_want = { .count = 1, .element = &oa_share_access_want },
.oa_open_claim = { .count = 1, .element = &oa_open_claim },
.oa_create_mode = { .count = 1, .element = &oa_create_mode },
};
static __be32 nfsd4_encode_fattr4_open_arguments(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
if (!xdrgen_encode_fattr4_open_arguments(xdr, &nfsd_open_arguments))
return nfserr_resource;
return nfs_ok;
}
static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_SUPPORTED_ATTRS] = nfsd4_encode_fattr4_supported_attrs,
[FATTR4_TYPE] = nfsd4_encode_fattr4_type,
@ -3487,6 +3557,7 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_MODE_UMASK] = nfsd4_encode_fattr4__noop,
[FATTR4_XATTR_SUPPORT] = nfsd4_encode_fattr4_xattr_support,
[FATTR4_OPEN_ARGUMENTS] = nfsd4_encode_fattr4_open_arguments,
};
/*
@ -3504,8 +3575,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
struct nfsd4_fattr_args args;
struct svc_fh *tempfh = NULL;
int starting_len = xdr->buf->len;
__be32 *attrlen_p, status;
int attrlen_offset;
unsigned int attrlen_offset;
__be32 attrlen, status;
u32 attrmask[3];
int err;
struct nfsd4_compoundres *resp = rqstp->rq_resp;
@ -3542,7 +3613,11 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
if (status)
goto out;
}
if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
if ((attrmask[0] & (FATTR4_WORD0_CHANGE |
FATTR4_WORD0_SIZE)) ||
(attrmask[1] & (FATTR4_WORD1_TIME_ACCESS |
FATTR4_WORD1_TIME_MODIFY |
FATTR4_WORD1_TIME_METADATA))) {
status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp);
if (status)
goto out;
@ -3554,11 +3629,22 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
if (dp) {
struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
if (ncf->ncf_file_modified)
if (ncf->ncf_file_modified) {
++ncf->ncf_initial_cinfo;
args.stat.size = ncf->ncf_cur_fsize;
if (!timespec64_is_epoch(&ncf->ncf_cb_mtime))
args.stat.mtime = ncf->ncf_cb_mtime;
}
args.change_attr = ncf->ncf_initial_cinfo;
if (!timespec64_is_epoch(&ncf->ncf_cb_atime))
args.stat.atime = ncf->ncf_cb_atime;
nfs4_put_stid(&dp->dl_stid);
} else {
args.change_attr = nfsd4_change_attribute(&args.stat);
}
if (err)
goto out_nfserr;
@ -3626,8 +3712,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
/* attr_vals */
attrlen_offset = xdr->buf->len;
attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
if (!attrlen_p)
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
goto out_resource;
bitmap_from_arr32(attr_bitmap, attrmask,
ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
@ -3637,7 +3722,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
if (status != nfs_ok)
goto out;
}
*attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
attrlen = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, XDR_UNIT);
status = nfs_ok;
out:
@ -4227,18 +4313,20 @@ nfsd4_encode_open_delegation4(struct xdr_stream *xdr, struct nfsd4_open *open)
if (xdr_stream_encode_u32(xdr, open->op_delegate_type) != XDR_UNIT)
return nfserr_resource;
switch (open->op_delegate_type) {
case NFS4_OPEN_DELEGATE_NONE:
case OPEN_DELEGATE_NONE:
status = nfs_ok;
break;
case NFS4_OPEN_DELEGATE_READ:
case OPEN_DELEGATE_READ:
case OPEN_DELEGATE_READ_ATTRS_DELEG:
/* read */
status = nfsd4_encode_open_read_delegation4(xdr, open);
break;
case NFS4_OPEN_DELEGATE_WRITE:
case OPEN_DELEGATE_WRITE:
case OPEN_DELEGATE_WRITE_ATTRS_DELEG:
/* write */
status = nfsd4_encode_open_write_delegation4(xdr, open);
break;
case NFS4_OPEN_DELEGATE_NONE_EXT:
case OPEN_DELEGATE_NONE_EXT:
/* od_whynone */
status = nfsd4_encode_open_none_delegation4(xdr, open);
break;
@ -4314,6 +4402,15 @@ static __be32 nfsd4_encode_splice_read(
int status, space_left;
__be32 nfserr;
/*
* Splice read doesn't work if encoding has already wandered
* into the XDR buf's page array.
*/
if (unlikely(xdr->buf->page_len)) {
WARN_ON_ONCE(1);
return nfserr_serverfault;
}
/*
* Make sure there is room at the end of buf->head for
* svcxdr_encode_opaque_pages() to create a tail buffer
@ -4396,25 +4493,23 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
struct nfsd4_read *read = &u->read;
struct xdr_stream *xdr = resp->xdr;
int starting_len = xdr->buf->len;
bool splice_ok = argp->splice_ok;
unsigned int eof_offset;
unsigned long maxcount;
__be32 wire_data[2];
struct file *file;
__be32 *p;
if (nfserr)
return nfserr;
eof_offset = xdr->buf->len;
file = read->rd_nf->nf_file;
p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
if (!p) {
/* Reserve space for the eof flag and byte count */
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2))) {
WARN_ON_ONCE(splice_ok);
return nfserr_resource;
}
if (resp->xdr->buf->page_len && splice_ok) {
WARN_ON_ONCE(1);
return nfserr_serverfault;
}
xdr_commit_encode(xdr);
maxcount = min_t(unsigned long, read->rd_length,
@ -4425,12 +4520,13 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
else
nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
if (nfserr) {
xdr_truncate_encode(xdr, starting_len);
xdr_truncate_encode(xdr, eof_offset);
return nfserr;
}
p = xdr_encode_bool(p, read->rd_eof);
*p = cpu_to_be32(read->rd_length);
wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
wire_data[1] = cpu_to_be32(read->rd_length);
write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
return nfs_ok;
}
@ -4439,25 +4535,21 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
union nfsd4_op_u *u)
{
struct nfsd4_readlink *readlink = &u->readlink;
__be32 *p, *maxcount_p, zero = xdr_zero;
__be32 *p, wire_count, zero = xdr_zero;
struct xdr_stream *xdr = resp->xdr;
int length_offset = xdr->buf->len;
unsigned int length_offset;
int maxcount, status;
maxcount_p = xdr_reserve_space(xdr, XDR_UNIT);
if (!maxcount_p)
/* linktext4.count */
length_offset = xdr->buf->len;
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
return nfserr_resource;
maxcount = PAGE_SIZE;
/* linktext4.data */
maxcount = PAGE_SIZE;
p = xdr_reserve_space(xdr, maxcount);
if (!p)
return nfserr_resource;
/*
* XXX: By default, vfs_readlink() will truncate symlinks if they
* would overflow the buffer. Is this kosher in NFSv4? If not, one
* easy fix is: if vfs_readlink() precisely fills the buffer, assume
* that truncation occurred, and return NFS4ERR_RESOURCE.
*/
nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
(char *)p, &maxcount);
if (nfserr == nfserr_isdir)
@ -4470,7 +4562,9 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
nfserr = nfserrno(status);
goto out_err;
}
*maxcount_p = cpu_to_be32(maxcount);
wire_count = cpu_to_be32(maxcount);
write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, XDR_UNIT);
xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount));
write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero,
xdr_pad_size(maxcount));
@ -4605,14 +4699,42 @@ nfsd4_encode_rpcsec_gss_info(struct xdr_stream *xdr,
}
static __be32
nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
nfsd4_encode_secinfo4(struct xdr_stream *xdr, rpc_authflavor_t pf,
u32 *supported)
{
struct rpcsec_gss_info info;
__be32 status;
if (rpcauth_get_gssinfo(pf, &info) == 0) {
(*supported)++;
/* flavor */
status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
if (status != nfs_ok)
return status;
/* flavor_info */
status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
if (status != nfs_ok)
return status;
} else if (pf < RPC_AUTH_MAXFLAVOR) {
(*supported)++;
/* flavor */
status = nfsd4_encode_uint32_t(xdr, pf);
if (status != nfs_ok)
return status;
}
return nfs_ok;
}
static __be32
nfsd4_encode_SECINFO4resok(struct xdr_stream *xdr, struct svc_export *exp)
{
u32 i, nflavs, supported;
struct exp_flavor_info *flavs;
struct exp_flavor_info def_flavs[2];
static bool report = true;
__be32 *flavorsp;
__be32 status;
unsigned int count_offset;
__be32 status, wire_count;
if (exp->ex_nflavors) {
flavs = exp->ex_flavors;
@ -4634,43 +4756,20 @@ nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
}
}
supported = 0;
flavorsp = xdr_reserve_space(xdr, XDR_UNIT);
if (!flavorsp)
count_offset = xdr->buf->len;
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
return nfserr_resource;
for (i = 0; i < nflavs; i++) {
rpc_authflavor_t pf = flavs[i].pseudoflavor;
struct rpcsec_gss_info info;
if (rpcauth_get_gssinfo(pf, &info) == 0) {
supported++;
/* flavor */
status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
if (status != nfs_ok)
return status;
/* flavor_info */
status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
if (status != nfs_ok)
return status;
} else if (pf < RPC_AUTH_MAXFLAVOR) {
supported++;
/* flavor */
status = nfsd4_encode_uint32_t(xdr, pf);
if (status != nfs_ok)
return status;
} else {
if (report)
pr_warn("NFS: SECINFO: security flavor %u "
"is not supported\n", pf);
}
for (i = 0, supported = 0; i < nflavs; i++) {
status = nfsd4_encode_secinfo4(xdr, flavs[i].pseudoflavor,
&supported);
if (status != nfs_ok)
return status;
}
if (nflavs != supported)
report = false;
*flavorsp = cpu_to_be32(supported);
wire_count = cpu_to_be32(supported);
write_bytes_to_xdr_buf(xdr->buf, count_offset, &wire_count,
XDR_UNIT);
return 0;
}
@ -4681,7 +4780,7 @@ nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_secinfo *secinfo = &u->secinfo;
struct xdr_stream *xdr = resp->xdr;
return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
return nfsd4_encode_SECINFO4resok(xdr, secinfo->si_exp);
}
static __be32
@ -4691,7 +4790,7 @@ nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name;
struct xdr_stream *xdr = resp->xdr;
return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
return nfsd4_encode_SECINFO4resok(xdr, secinfo->sin_exp);
}
static __be32
@ -4966,7 +5065,7 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
if (nfserr != nfs_ok)
return nfserr;
/* sr_target_highest_slotid */
nfserr = nfsd4_encode_slotid4(xdr, seq->maxslots - 1);
nfserr = nfsd4_encode_slotid4(xdr, seq->target_maxslots - 1);
if (nfserr != nfs_ok)
return nfserr;
/* sr_status_flags */
@ -5294,17 +5393,20 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
struct file *file = read->rd_nf->nf_file;
struct xdr_stream *xdr = resp->xdr;
bool splice_ok = argp->splice_ok;
unsigned int offset_offset;
__be32 nfserr, wire_count;
unsigned long maxcount;
__be32 nfserr, *p;
__be64 wire_offset;
/* Content type, offset, byte count */
p = xdr_reserve_space(xdr, 4 + 8 + 4);
if (!p)
if (xdr_stream_encode_u32(xdr, NFS4_CONTENT_DATA) != XDR_UNIT)
return nfserr_io;
if (resp->xdr->buf->page_len && splice_ok) {
WARN_ON_ONCE(splice_ok);
return nfserr_serverfault;
}
offset_offset = xdr->buf->len;
/* Reserve space for the byte offset and count */
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 3)))
return nfserr_io;
xdr_commit_encode(xdr);
maxcount = min_t(unsigned long, read->rd_length,
(xdr->buf->buflen - xdr->buf->len));
@ -5316,10 +5418,12 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
if (nfserr)
return nfserr;
*p++ = cpu_to_be32(NFS4_CONTENT_DATA);
p = xdr_encode_hyper(p, read->rd_offset);
*p = cpu_to_be32(read->rd_length);
wire_offset = cpu_to_be64(read->rd_offset);
write_bytes_to_xdr_buf(xdr->buf, offset_offset, &wire_offset,
XDR_UNIT * 2);
wire_count = cpu_to_be32(read->rd_length);
write_bytes_to_xdr_buf(xdr->buf, offset_offset + XDR_UNIT * 2,
&wire_count, XDR_UNIT);
return nfs_ok;
}
@ -5330,16 +5434,17 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_read *read = &u->read;
struct file *file = read->rd_nf->nf_file;
struct xdr_stream *xdr = resp->xdr;
int starting_len = xdr->buf->len;
unsigned int eof_offset;
__be32 wire_data[2];
u32 segments = 0;
__be32 *p;
if (nfserr)
return nfserr;
/* eof flag, segment count */
p = xdr_reserve_space(xdr, 4 + 4);
if (!p)
eof_offset = xdr->buf->len;
/* Reserve space for the eof flag and segment count */
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2)))
return nfserr_io;
xdr_commit_encode(xdr);
@ -5349,15 +5454,16 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
nfserr = nfsd4_encode_read_plus_data(resp, read);
if (nfserr) {
xdr_truncate_encode(xdr, starting_len);
xdr_truncate_encode(xdr, eof_offset);
return nfserr;
}
segments++;
out:
p = xdr_encode_bool(p, read->rd_eof);
*p = cpu_to_be32(segments);
wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
wire_data[1] = cpu_to_be32(segments);
write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
return nfserr;
}
@ -5758,15 +5864,14 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
struct nfs4_stateowner *so = resp->cstate.replay_owner;
struct svc_rqst *rqstp = resp->rqstp;
const struct nfsd4_operation *opdesc = op->opdesc;
int post_err_offset;
unsigned int op_status_offset;
nfsd4_enc encoder;
__be32 *p;
p = xdr_reserve_space(xdr, 8);
if (!p)
if (xdr_stream_encode_u32(xdr, op->opnum) != XDR_UNIT)
goto release;
op_status_offset = xdr->buf->len;
if (!xdr_reserve_space(xdr, XDR_UNIT))
goto release;
*p++ = cpu_to_be32(op->opnum);
post_err_offset = xdr->buf->len;
if (op->opnum == OP_ILLEGAL)
goto status;
@ -5807,20 +5912,21 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
* bug if we had to do this on a non-idempotent op:
*/
warn_on_nonidempotent_op(op);
xdr_truncate_encode(xdr, post_err_offset);
xdr_truncate_encode(xdr, op_status_offset + XDR_UNIT);
}
if (so) {
int len = xdr->buf->len - post_err_offset;
int len = xdr->buf->len - (op_status_offset + XDR_UNIT);
so->so_replay.rp_status = op->status;
so->so_replay.rp_buflen = len;
read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
read_bytes_from_xdr_buf(xdr->buf, op_status_offset + XDR_UNIT,
so->so_replay.rp_buf, len);
}
status:
op->status = nfsd4_map_status(op->status,
resp->cstate.minorversion);
*p = op->status;
write_bytes_to_xdr_buf(xdr->buf, op_status_offset,
&op->status, XDR_UNIT);
release:
if (opdesc && opdesc->op_release)
opdesc->op_release(&op->u);

256
fs/nfsd/nfs4xdr_gen.c Normal file

@ -0,0 +1,256 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x
// XDR specification modification time: Mon Oct 14 09:10:13 2024
#include <linux/sunrpc/svc.h>
#include "nfs4xdr_gen.h"
static bool __maybe_unused
xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
{
return xdrgen_decode_hyper(xdr, ptr);
};
static bool __maybe_unused
xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
{
return xdrgen_decode_unsigned_int(xdr, ptr);
};
static bool __maybe_unused
xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
{
if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
return false;
for (u32 i = 0; i < ptr->count; i++)
if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i]))
return false;
return true;
};
static bool __maybe_unused
xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
{
if (!xdrgen_decode_int64_t(xdr, &ptr->seconds))
return false;
if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds))
return false;
return true;
};
static bool __maybe_unused
xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
{
return xdrgen_decode_bool(xdr, ptr);
};
static bool __maybe_unused
xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr)
{
if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access))
return false;
if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_deny))
return false;
if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access_want))
return false;
if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_open_claim))
return false;
if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode))
return false;
return true;
};
static bool __maybe_unused
xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, open_args_share_deny4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, open_args_share_access_want4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, open_args_open_claim4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, open_args_createmode4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
bool
xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr)
{
return xdrgen_decode_open_arguments4(xdr, ptr);
};
bool
xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
{
return xdrgen_decode_nfstime4(xdr, ptr);
};
bool
xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr)
{
return xdrgen_decode_nfstime4(xdr, ptr);
};
static bool __maybe_unused
xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
{
return xdrgen_encode_hyper(xdr, value);
};
static bool __maybe_unused
xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
{
return xdrgen_encode_unsigned_int(xdr, value);
};
static bool __maybe_unused
xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
{
if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value.count; i++)
if (!xdrgen_encode_uint32_t(xdr, value.element[i]))
return false;
return true;
};
static bool __maybe_unused
xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
{
if (!xdrgen_encode_int64_t(xdr, value->seconds))
return false;
if (!xdrgen_encode_uint32_t(xdr, value->nseconds))
return false;
return true;
};
static bool __maybe_unused
xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
{
return xdrgen_encode_bool(xdr, value);
};
static bool __maybe_unused
xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value)
{
if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access))
return false;
if (!xdrgen_encode_bitmap4(xdr, value->oa_share_deny))
return false;
if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access_want))
return false;
if (!xdrgen_encode_bitmap4(xdr, value->oa_open_claim))
return false;
if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode))
return false;
return true;
};
static bool __maybe_unused
xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_open_args_share_deny4(struct xdr_stream *xdr, open_args_share_deny4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_open_args_share_access_want4(struct xdr_stream *xdr, open_args_share_access_want4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_open_args_open_claim4(struct xdr_stream *xdr, open_args_open_claim4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_open_args_createmode4(struct xdr_stream *xdr, open_args_createmode4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
bool
xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments *value)
{
return xdrgen_encode_open_arguments4(xdr, value);
};
bool
xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access *value)
{
return xdrgen_encode_nfstime4(xdr, value);
};
bool
xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value)
{
return xdrgen_encode_nfstime4(xdr, value);
};
static bool __maybe_unused
xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}

25
fs/nfsd/nfs4xdr_gen.h Normal file

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */
#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/xdrgen/_defs.h>
#include <linux/sunrpc/xdrgen/_builtins.h>
#include <linux/sunrpc/xdrgen/nfs4_1.h>
bool xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr);
bool xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments *value);
bool xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr);
bool xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access *value);
bool xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr);
bool xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value);
#endif /* _LINUX_XDRGEN_NFS4_1_DECL_H */

@ -48,7 +48,6 @@ enum {
NFSD_Versions,
NFSD_Ports,
NFSD_MaxBlkSize,
NFSD_MaxConnections,
NFSD_Filecache,
NFSD_Leasetime,
NFSD_Gracetime,
@ -68,7 +67,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size);
static ssize_t write_versions(struct file *file, char *buf, size_t size);
static ssize_t write_ports(struct file *file, char *buf, size_t size);
static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
static ssize_t write_maxconn(struct file *file, char *buf, size_t size);
#ifdef CONFIG_NFSD_V4
static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
static ssize_t write_gracetime(struct file *file, char *buf, size_t size);
@ -87,7 +85,6 @@ static ssize_t (*const write_op[])(struct file *, char *, size_t) = {
[NFSD_Versions] = write_versions,
[NFSD_Ports] = write_ports,
[NFSD_MaxBlkSize] = write_maxblksize,
[NFSD_MaxConnections] = write_maxconn,
#ifdef CONFIG_NFSD_V4
[NFSD_Leasetime] = write_leasetime,
[NFSD_Gracetime] = write_gracetime,
@ -902,44 +899,6 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
nfsd_max_blksize);
}
/*
* write_maxconn - Set or report the current max number of connections
*
* Input:
* buf: ignored
* size: zero
* OR
*
* Input:
* buf: C string containing an unsigned
* integer value representing the new
* number of max connections
* size: non-zero length of C string in @buf
* Output:
* On success: passed-in buffer filled with '\n'-terminated C string
* containing numeric value of max_connections setting
* for this net namespace;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
static ssize_t write_maxconn(struct file *file, char *buf, size_t size)
{
char *mesg = buf;
struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
unsigned int maxconn = nn->max_connections;
if (size > 0) {
int rv = get_uint(&mesg, &maxconn);
if (rv)
return rv;
trace_nfsd_ctl_maxconn(netns(file), maxconn);
nn->max_connections = maxconn;
}
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%u\n", maxconn);
}
#ifdef CONFIG_NFSD_V4
static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
time64_t *time, struct nfsd_net *nn)
@ -1372,7 +1331,6 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_Filecache] = {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO},
#ifdef CONFIG_NFSD_V4
[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},

@ -88,9 +88,6 @@ struct nfsd_genl_rqstp {
extern struct svc_program nfsd_programs[];
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
extern struct mutex nfsd_mutex;
extern spinlock_t nfsd_drc_lock;
extern unsigned long nfsd_drc_max_mem;
extern unsigned long nfsd_drc_mem_used;
extern atomic_t nfsd_th_cnt; /* number of available threads */
extern const struct seq_operations nfs_exports_op;
@ -458,7 +455,10 @@ enum {
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_MODE_UMASK | \
NFSD4_2_SECURITY_ATTRS | \
FATTR4_WORD2_XATTR_SUPPORT)
FATTR4_WORD2_XATTR_SUPPORT | \
FATTR4_WORD2_TIME_DELEG_ACCESS | \
FATTR4_WORD2_TIME_DELEG_MODIFY | \
FATTR4_WORD2_OPEN_ARGUMENTS)
extern const u32 nfsd_suppattrs[3][3];
@ -528,7 +528,10 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
#endif
#define NFSD_WRITEABLE_ATTRS_WORD2 \
(FATTR4_WORD2_MODE_UMASK \
| MAYBE_FATTR4_WORD2_SECURITY_LABEL)
| MAYBE_FATTR4_WORD2_SECURITY_LABEL \
| FATTR4_WORD2_TIME_DELEG_ACCESS \
| FATTR4_WORD2_TIME_DELEG_MODIFY \
)
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
NFSD_WRITEABLE_ATTRS_WORD0

@ -381,6 +381,8 @@ __fh_verify(struct svc_rqst *rqstp,
if (error)
goto out;
svc_xprt_set_valid(rqstp->rq_xprt);
/* Finally, check access permissions. */
error = nfsd_permission(cred, exp, dentry, access);
out:

@ -70,16 +70,6 @@ static __be32 nfsd_init_request(struct svc_rqst *,
*/
DEFINE_MUTEX(nfsd_mutex);
/*
* nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used.
* nfsd_drc_max_pages limits the total amount of memory available for
* version 4.1 DRC caches.
* nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
*/
DEFINE_SPINLOCK(nfsd_drc_lock);
unsigned long nfsd_drc_max_mem;
unsigned long nfsd_drc_mem_used;
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
static const struct svc_version *localio_versions[] = {
[1] = &localio_version1,
@ -575,27 +565,6 @@ void nfsd_reset_versions(struct nfsd_net *nn)
}
}
/*
* Each session guarantees a negotiated per slot memory cache for replies
* which in turn consumes memory beyond the v2/v3/v4.0 server. A dedicated
* NFSv4.1 server might want to use more memory for a DRC than a machine
* with mutiple services.
*
* Impose a hard limit on the number of pages for the DRC which varies
* according to the machines free pages. This is of course only a default.
*
* For now this is a #defined shift which could be under admin control
* in the future.
*/
static void set_max_drc(void)
{
#define NFSD_DRC_SIZE_SHIFT 7
nfsd_drc_max_mem = (nr_free_buffer_pages()
>> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
nfsd_drc_mem_used = 0;
dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem);
}
static int nfsd_get_default_max_blksize(void)
{
struct sysinfo i;
@ -668,7 +637,6 @@ int nfsd_create_serv(struct net *net)
if (serv == NULL)
return -ENOMEM;
serv->sv_maxconn = nn->max_connections;
error = svc_bind(serv, net);
if (error < 0) {
svc_destroy(&serv);
@ -678,7 +646,6 @@ int nfsd_create_serv(struct net *net)
nn->nfsd_serv = serv;
spin_unlock(&nfsd_notifier_lock);
set_max_drc();
/* check if the notifier is already set */
if (atomic_inc_return(&nfsd_notifier_refcount) == 1) {
register_inetaddr_notifier(&nfsd_inetaddr_notifier);
@ -954,11 +921,7 @@ nfsd(void *vrqstp)
* The main request loop
*/
while (!svc_thread_should_stop(rqstp)) {
/* Update sv_maxconn if it has changed */
rqstp->rq_server->sv_maxconn = nn->max_connections;
svc_recv(rqstp);
nfsd_file_net_dispose(nn);
}

@ -159,6 +159,8 @@ struct nfs4_cb_fattr {
/* from CB_GETATTR reply */
u64 ncf_cb_change;
u64 ncf_cb_fsize;
struct timespec64 ncf_cb_mtime;
struct timespec64 ncf_cb_atime;
unsigned long ncf_cb_flags;
bool ncf_file_modified;
@ -207,6 +209,22 @@ struct nfs4_delegation {
struct nfs4_cb_fattr dl_cb_fattr;
};
static inline bool deleg_is_read(u32 dl_type)
{
return (dl_type == OPEN_DELEGATE_READ || dl_type == OPEN_DELEGATE_READ_ATTRS_DELEG);
}
static inline bool deleg_is_write(u32 dl_type)
{
return (dl_type == OPEN_DELEGATE_WRITE || dl_type == OPEN_DELEGATE_WRITE_ATTRS_DELEG);
}
static inline bool deleg_attrs_deleg(u32 dl_type)
{
return dl_type == OPEN_DELEGATE_READ_ATTRS_DELEG ||
dl_type == OPEN_DELEGATE_WRITE_ATTRS_DELEG;
}
#define cb_to_delegation(cb) \
container_of(cb, struct nfs4_delegation, dl_recall)
@ -227,8 +245,11 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
return container_of(s, struct nfs4_delegation, dl_stid);
}
/* Maximum number of slots per session. 160 is useful for long haul TCP */
#define NFSD_MAX_SLOTS_PER_SESSION 160
/* Maximum number of slots per session. This is for sanity-check only.
* It could be increased if we had a mechanism to shutdown misbehaving clients.
* A large number can be needed to get good throughput on high-latency servers.
*/
#define NFSD_MAX_SLOTS_PER_SESSION 2048
/* Maximum session per slot cache size */
#define NFSD_SLOT_CACHE_SIZE 2048
/* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */
@ -242,10 +263,12 @@ struct nfsd4_slot {
struct svc_cred sl_cred;
u32 sl_datalen;
u16 sl_opcnt;
u16 sl_generation;
#define NFSD4_SLOT_INUSE (1 << 0)
#define NFSD4_SLOT_CACHETHIS (1 << 1)
#define NFSD4_SLOT_INITIALIZED (1 << 2)
#define NFSD4_SLOT_CACHED (1 << 3)
#define NFSD4_SLOT_REUSED (1 << 4)
u8 sl_flags;
char sl_data[];
};
@ -318,16 +341,19 @@ struct nfsd4_session {
u32 se_cb_slot_avail; /* bitmap of available slots */
u32 se_cb_highest_slot; /* highest slot client wants */
u32 se_cb_prog;
bool se_dead;
struct list_head se_hash; /* hash by sessionid */
struct list_head se_perclnt;
struct list_head se_all_sessions;/* global list of sessions */
struct nfs4_client *se_client;
struct nfs4_sessionid se_sessionid;
struct nfsd4_channel_attrs se_fchannel;
struct nfsd4_cb_sec se_cb_sec;
struct list_head se_conns;
u32 se_cb_seq_nr[NFSD_BC_SLOT_TABLE_SIZE];
struct nfsd4_slot *se_slots[]; /* forward channel slots */
struct xarray se_slots; /* forward channel slots */
u16 se_slot_gen;
bool se_dead;
u32 se_target_maxslots;
};
/* formatted contents of nfs4_sessionid */
@ -505,7 +531,7 @@ struct nfs4_replay {
unsigned int rp_buflen;
char *rp_buf;
struct knfsd_fh rp_openfh;
atomic_t rp_locked;
int rp_locked;
char rp_ibuf[NFSD4_REPLAY_ISIZE];
};

@ -626,7 +626,6 @@ DEFINE_STATEID_EVENT(open);
DEFINE_STATEID_EVENT(deleg_read);
DEFINE_STATEID_EVENT(deleg_write);
DEFINE_STATEID_EVENT(deleg_return);
DEFINE_STATEID_EVENT(deleg_recall);
DECLARE_EVENT_CLASS(nfsd_stateseqid_class,
TP_PROTO(u32 seqid, const stateid_t *stp),

@ -576,9 +576,7 @@ struct nfsd4_sequence {
u32 slotid; /* request/response */
u32 maxslots; /* request/response */
u32 cachethis; /* request */
#if 0
u32 target_maxslots; /* response */
#endif /* not yet */
u32 status_flags; /* response */
};

@ -59,16 +59,20 @@
* 1: CB_GETATTR opcode (32-bit)
* N: file_handle
* 1: number of entry in attribute array (32-bit)
* 1: entry 0 in attribute array (32-bit)
* 3: entry 0-2 in attribute array (32-bit * 3)
*/
#define NFS4_enc_cb_getattr_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + enc_nfs4_fh_sz + 1 + 1)
1 + enc_nfs4_fh_sz + 1 + 3)
/*
* 4: fattr_bitmap_maxsz
* 1: attribute array len
* 2: change attr (64-bit)
* 2: size (64-bit)
* 2: atime.seconds (64-bit)
* 1: atime.nanoseconds (32-bit)
* 2: mtime.seconds (64-bit)
* 1: mtime.nanoseconds (32-bit)
*/
#define NFS4_dec_cb_getattr_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + 4 + 1 + 2 + 2 + op_dec_sz)
cb_sequence_dec_sz + 4 + 1 + 2 + 2 + 2 + 1 + 2 + 1 + op_dec_sz)

@ -17,6 +17,7 @@
#include <linux/uidgid.h>
#include <uapi/linux/nfs4.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/xdrgen/nfs4_1.h>
enum nfs4_acl_whotype {
NFS4_ACL_WHO_NAMED = 0,
@ -365,7 +366,7 @@ enum limit_by4 {
NFS4_LIMIT_BLOCKS = 2
};
enum open_delegation_type4 {
enum nfs4_open_delegation_type4 {
NFS4_OPEN_DELEGATE_NONE = 0,
NFS4_OPEN_DELEGATE_READ = 1,
NFS4_OPEN_DELEGATE_WRITE = 2,
@ -512,12 +513,6 @@ enum {
FATTR4_XATTR_SUPPORT = 82,
};
enum {
FATTR4_TIME_DELEG_ACCESS = 84,
FATTR4_TIME_DELEG_MODIFY = 85,
FATTR4_OPEN_ARGUMENTS = 86,
};
/*
* The following internal definitions enable processing the above
* attribute bits within 32-bit word boundaries.

@ -1315,11 +1315,6 @@ struct nfs4_fsid_present_res {
#endif /* CONFIG_NFS_V4 */
struct nfstime4 {
u64 seconds;
u32 nseconds;
};
#ifdef CONFIG_NFS_V4_1
struct pnfs_commit_bucket {

@ -222,6 +222,8 @@ static inline bool cache_is_expired(struct cache_detail *detail, struct cache_he
return detail->flush_time >= h->last_refresh;
}
extern int cache_check_rcu(struct cache_detail *detail,
struct cache_head *h, struct cache_req *rqstp);
extern int cache_check(struct cache_detail *detail,
struct cache_head *h, struct cache_req *rqstp);
extern void cache_flush(void);

@ -1,81 +0,0 @@
/*
* linux/include/linux/sunrpc/gss_asn1.h
*
* minimal asn1 for generic encoding/decoding of gss tokens
*
* Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
* lib/gssapi/krb5/gssapiP_krb5.h, and others
*
* Copyright (c) 2000 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*/
/*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
*/
#include <linux/sunrpc/gss_api.h>
#define SIZEOF_INT 4
/* from gssapi_err_generic.h */
#define G_BAD_SERVICE_NAME (-2045022976L)
#define G_BAD_STRING_UID (-2045022975L)
#define G_NOUSER (-2045022974L)
#define G_VALIDATE_FAILED (-2045022973L)
#define G_BUFFER_ALLOC (-2045022972L)
#define G_BAD_MSG_CTX (-2045022971L)
#define G_WRONG_SIZE (-2045022970L)
#define G_BAD_USAGE (-2045022969L)
#define G_UNKNOWN_QOP (-2045022968L)
#define G_NO_HOSTNAME (-2045022967L)
#define G_BAD_HOSTNAME (-2045022966L)
#define G_WRONG_MECH (-2045022965L)
#define G_BAD_TOK_HEADER (-2045022964L)
#define G_BAD_DIRECTION (-2045022963L)
#define G_TOK_TRUNC (-2045022962L)
#define G_REFLECT (-2045022961L)
#define G_WRONG_TOKID (-2045022960L)
#define g_OID_equal(o1,o2) \
(((o1)->len == (o2)->len) && \
(memcmp((o1)->data,(o2)->data,(int) (o1)->len) == 0))
u32 g_verify_token_header(
struct xdr_netobj *mech,
int *body_size,
unsigned char **buf_in,
int toksize);
int g_token_size(
struct xdr_netobj *mech,
unsigned int body_size);
void g_make_token_header(
struct xdr_netobj *mech,
int body_size,
unsigned char **buf);

@ -40,7 +40,6 @@
#include <crypto/skcipher.h>
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/gss_err.h>
#include <linux/sunrpc/gss_asn1.h>
/* Length of constant used in key derivation */
#define GSS_KRB5_K5CLENGTH (5)

@ -72,16 +72,12 @@ struct svc_serv {
spinlock_t sv_lock;
unsigned int sv_nprogs; /* Number of sv_programs */
unsigned int sv_nrthreads; /* # of server threads */
unsigned int sv_maxconn; /* max connections allowed or
* '0' causing max to be based
* on number of threads. */
unsigned int sv_max_payload; /* datagram payload size */
unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */
unsigned int sv_xdrsize; /* XDR buffer size */
struct list_head sv_permsocks; /* all permanent sockets */
struct list_head sv_tempsocks; /* all temporary sockets */
int sv_tmpcnt; /* count of temporary sockets */
int sv_tmpcnt; /* count of temporary "valid" sockets */
struct timer_list sv_temptimer; /* timer for aging temporary sockets */
char * sv_name; /* service name */
@ -327,12 +323,7 @@ static inline bool svc_thread_should_stop(struct svc_rqst *rqstp)
*/
static inline void svc_thread_init_status(struct svc_rqst *rqstp, int err)
{
rqstp->rq_err = err;
/* memory barrier ensures assignment to error above is visible before
* waitqueue_active() test below completes.
*/
smp_mb();
wake_up_var(&rqstp->rq_err);
store_release_wake_up(&rqstp->rq_err, err);
if (err)
kthread_exit(1);
}

@ -99,8 +99,30 @@ enum {
XPT_HANDSHAKE, /* xprt requests a handshake */
XPT_TLS_SESSION, /* transport-layer security established */
XPT_PEER_AUTH, /* peer has been authenticated */
XPT_PEER_VALID, /* peer has presented a filehandle that
* it has access to. It is NOT counted
* in ->sv_tmpcnt.
*/
};
/*
* Maximum number of "tmp" connections - those without XPT_PEER_VALID -
* permitted on any service.
*/
#define XPT_MAX_TMP_CONN 64
static inline void svc_xprt_set_valid(struct svc_xprt *xpt)
{
if (test_bit(XPT_TEMP, &xpt->xpt_flags) &&
!test_and_set_bit(XPT_PEER_VALID, &xpt->xpt_flags)) {
struct svc_serv *serv = xpt->xpt_server;
spin_lock(&serv->sv_lock);
serv->sv_tmpcnt -= 1;
spin_unlock(&serv->sv_lock);
}
}
static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
{
spin_lock(&xpt->xpt_lock);

@ -0,0 +1,153 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */
#ifndef _LINUX_XDRGEN_NFS4_1_DEF_H
#define _LINUX_XDRGEN_NFS4_1_DEF_H
#include <linux/types.h>
#include <linux/sunrpc/xdrgen/_defs.h>
typedef s64 int64_t;
typedef u32 uint32_t;
typedef struct {
u32 count;
uint32_t *element;
} bitmap4;
struct nfstime4 {
int64_t seconds;
uint32_t nseconds;
};
typedef bool fattr4_offline;
enum { FATTR4_OFFLINE = 83 };
struct open_arguments4 {
bitmap4 oa_share_access;
bitmap4 oa_share_deny;
bitmap4 oa_share_access_want;
bitmap4 oa_open_claim;
bitmap4 oa_create_mode;
};
enum open_args_share_access4 {
OPEN_ARGS_SHARE_ACCESS_READ = 1,
OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
OPEN_ARGS_SHARE_ACCESS_BOTH = 3,
};
typedef enum open_args_share_access4 open_args_share_access4;
enum open_args_share_deny4 {
OPEN_ARGS_SHARE_DENY_NONE = 0,
OPEN_ARGS_SHARE_DENY_READ = 1,
OPEN_ARGS_SHARE_DENY_WRITE = 2,
OPEN_ARGS_SHARE_DENY_BOTH = 3,
};
typedef enum open_args_share_deny4 open_args_share_deny4;
enum open_args_share_access_want4 {
OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG = 3,
OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG = 4,
OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL = 5,
OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 17,
OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 18,
OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20,
OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21,
};
typedef enum open_args_share_access_want4 open_args_share_access_want4;
enum open_args_open_claim4 {
OPEN_ARGS_OPEN_CLAIM_NULL = 0,
OPEN_ARGS_OPEN_CLAIM_PREVIOUS = 1,
OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR = 2,
OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
OPEN_ARGS_OPEN_CLAIM_FH = 4,
OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5,
OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6,
};
typedef enum open_args_open_claim4 open_args_open_claim4;
enum open_args_createmode4 {
OPEN_ARGS_CREATEMODE_UNCHECKED4 = 0,
OPEN_ARGS_CREATE_MODE_GUARDED = 1,
OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2,
OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3,
};
typedef enum open_args_createmode4 open_args_createmode4;
typedef struct open_arguments4 fattr4_open_arguments;
enum { FATTR4_OPEN_ARGUMENTS = 86 };
enum { OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010 };
typedef struct nfstime4 fattr4_time_deleg_access;
typedef struct nfstime4 fattr4_time_deleg_modify;
enum { FATTR4_TIME_DELEG_ACCESS = 84 };
enum { FATTR4_TIME_DELEG_MODIFY = 85 };
enum { OPEN4_SHARE_ACCESS_WANT_DELEG_MASK = 0xFF00 };
enum { OPEN4_SHARE_ACCESS_WANT_NO_PREFERENCE = 0x0000 };
enum { OPEN4_SHARE_ACCESS_WANT_READ_DELEG = 0x0100 };
enum { OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG = 0x0200 };
enum { OPEN4_SHARE_ACCESS_WANT_ANY_DELEG = 0x0300 };
enum { OPEN4_SHARE_ACCESS_WANT_NO_DELEG = 0x0400 };
enum { OPEN4_SHARE_ACCESS_WANT_CANCEL = 0x0500 };
enum { OPEN4_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 0x10000 };
enum { OPEN4_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 0x20000 };
enum { OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000 };
enum { OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000 };
enum open_delegation_type4 {
OPEN_DELEGATE_NONE = 0,
OPEN_DELEGATE_READ = 1,
OPEN_DELEGATE_WRITE = 2,
OPEN_DELEGATE_NONE_EXT = 3,
OPEN_DELEGATE_READ_ATTRS_DELEG = 4,
OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5,
};
typedef enum open_delegation_type4 open_delegation_type4;
#define NFS4_int64_t_sz \
(XDR_hyper)
#define NFS4_uint32_t_sz \
(XDR_unsigned_int)
#define NFS4_bitmap4_sz (XDR_unsigned_int)
#define NFS4_nfstime4_sz \
(NFS4_int64_t_sz + NFS4_uint32_t_sz)
#define NFS4_fattr4_offline_sz \
(XDR_bool)
#define NFS4_open_arguments4_sz \
(NFS4_bitmap4_sz + NFS4_bitmap4_sz + NFS4_bitmap4_sz + NFS4_bitmap4_sz + NFS4_bitmap4_sz)
#define NFS4_open_args_share_access4_sz (XDR_int)
#define NFS4_open_args_share_deny4_sz (XDR_int)
#define NFS4_open_args_share_access_want4_sz (XDR_int)
#define NFS4_open_args_open_claim4_sz (XDR_int)
#define NFS4_open_args_createmode4_sz (XDR_int)
#define NFS4_fattr4_open_arguments_sz \
(NFS4_open_arguments4_sz)
#define NFS4_fattr4_time_deleg_access_sz \
(NFS4_nfstime4_sz)
#define NFS4_fattr4_time_deleg_modify_sz \
(NFS4_nfstime4_sz)
#define NFS4_open_delegation_type4_sz (XDR_int)
#endif /* _LINUX_XDRGEN_NFS4_1_DEF_H */

@ -75,7 +75,6 @@ extern struct rpc_xprt_switch *xprt_iter_xchg_switch(
struct rpc_xprt_switch *newswitch);
extern struct rpc_xprt *xprt_iter_xprt(struct rpc_xprt_iter *xpi);
extern struct rpc_xprt *xprt_iter_get_xprt(struct rpc_xprt_iter *xpi);
extern struct rpc_xprt *xprt_iter_get_next(struct rpc_xprt_iter *xpi);
extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,

@ -49,6 +49,11 @@ static inline int timespec64_equal(const struct timespec64 *a,
return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec);
}
static inline bool timespec64_is_epoch(const struct timespec64 *ts)
{
return ts->tv_sec == 0 && ts->tv_nsec == 0;
}
/*
* lhs < rhs: return <0
* lhs == rhs: return 0

@ -58,7 +58,7 @@
#define NFS4_SHARE_DENY_BOTH 0x0003
/* nfs41 */
#define NFS4_SHARE_WANT_MASK 0xFF00
#define NFS4_SHARE_WANT_TYPE_MASK 0xFF00
#define NFS4_SHARE_WANT_NO_PREFERENCE 0x0000
#define NFS4_SHARE_WANT_READ_DELEG 0x0100
#define NFS4_SHARE_WANT_WRITE_DELEG 0x0200
@ -66,13 +66,16 @@
#define NFS4_SHARE_WANT_NO_DELEG 0x0400
#define NFS4_SHARE_WANT_CANCEL 0x0500
#define NFS4_SHARE_WHEN_MASK 0xF0000
#define NFS4_SHARE_WHEN_MASK 0xF0000
#define NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL 0x10000
#define NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED 0x20000
#define NFS4_SHARE_WANT_MOD_MASK 0xF00000
#define NFS4_SHARE_WANT_DELEG_TIMESTAMPS 0x100000
#define NFS4_SHARE_WANT_OPEN_XOR_DELEGATION 0x200000
#define NFS4_SHARE_WANT_MASK (NFS4_SHARE_WANT_TYPE_MASK | NFS4_SHARE_WANT_MOD_MASK)
#define NFS4_CDFC4_FORE 0x1
#define NFS4_CDFC4_BACK 0x2
#define NFS4_CDFC4_BOTH 0x3

@ -5,7 +5,7 @@
obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
auth_rpcgss-y := auth_gss.o gss_generic_token.o \
auth_rpcgss-y := auth_gss.o \
gss_mech_switch.o svcauth_gss.o \
gss_rpc_upcall.o gss_rpc_xdr.o trace.o

@ -1,231 +0,0 @@
/*
* linux/net/sunrpc/gss_generic_token.c
*
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/generic/util_token.c
*
* Copyright (c) 2000 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*/
/*
* Copyright 1993 by OpenVision Technologies, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appears in all copies and
* that both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of OpenVision not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. OpenVision makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/gss_asn1.h>
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/* TWRITE_STR from gssapiP_generic.h */
#define TWRITE_STR(ptr, str, len) \
memcpy((ptr), (char *) (str), (len)); \
(ptr) += (len);
/* XXXX this code currently makes the assumption that a mech oid will
never be longer than 127 bytes. This assumption is not inherent in
the interfaces, so the code can be fixed if the OSI namespace
balloons unexpectedly. */
/* Each token looks like this:
0x60 tag for APPLICATION 0, SEQUENCE
(constructed, definite-length)
<length> possible multiple bytes, need to parse/generate
0x06 tag for OBJECT IDENTIFIER
<moid_length> compile-time constant string (assume 1 byte)
<moid_bytes> compile-time constant string
<inner_bytes> the ANY containing the application token
bytes 0,1 are the token type
bytes 2,n are the token data
For the purposes of this abstraction, the token "header" consists of
the sequence tag and length octets, the mech OID DER encoding, and the
first two inner bytes, which indicate the token type. The token
"body" consists of everything else.
*/
static int
der_length_size( int length)
{
if (length < (1<<7))
return 1;
else if (length < (1<<8))
return 2;
#if (SIZEOF_INT == 2)
else
return 3;
#else
else if (length < (1<<16))
return 3;
else if (length < (1<<24))
return 4;
else
return 5;
#endif
}
static void
der_write_length(unsigned char **buf, int length)
{
if (length < (1<<7)) {
*(*buf)++ = (unsigned char) length;
} else {
*(*buf)++ = (unsigned char) (der_length_size(length)+127);
#if (SIZEOF_INT > 2)
if (length >= (1<<24))
*(*buf)++ = (unsigned char) (length>>24);
if (length >= (1<<16))
*(*buf)++ = (unsigned char) ((length>>16)&0xff);
#endif
if (length >= (1<<8))
*(*buf)++ = (unsigned char) ((length>>8)&0xff);
*(*buf)++ = (unsigned char) (length&0xff);
}
}
/* returns decoded length, or < 0 on failure. Advances buf and
decrements bufsize */
static int
der_read_length(unsigned char **buf, int *bufsize)
{
unsigned char sf;
int ret;
if (*bufsize < 1)
return -1;
sf = *(*buf)++;
(*bufsize)--;
if (sf & 0x80) {
if ((sf &= 0x7f) > ((*bufsize)-1))
return -1;
if (sf > SIZEOF_INT)
return -1;
ret = 0;
for (; sf; sf--) {
ret = (ret<<8) + (*(*buf)++);
(*bufsize)--;
}
} else {
ret = sf;
}
return ret;
}
/* returns the length of a token, given the mech oid and the body size */
int
g_token_size(struct xdr_netobj *mech, unsigned int body_size)
{
/* set body_size to sequence contents size */
body_size += 2 + (int) mech->len; /* NEED overflow check */
return 1 + der_length_size(body_size) + body_size;
}
EXPORT_SYMBOL_GPL(g_token_size);
/* fills in a buffer with the token header. The buffer is assumed to
be the right size. buf is advanced past the token header */
void
g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf)
{
*(*buf)++ = 0x60;
der_write_length(buf, 2 + mech->len + body_size);
*(*buf)++ = 0x06;
*(*buf)++ = (unsigned char) mech->len;
TWRITE_STR(*buf, mech->data, ((int) mech->len));
}
EXPORT_SYMBOL_GPL(g_make_token_header);
/*
* Given a buffer containing a token, reads and verifies the token,
* leaving buf advanced past the token header, and setting body_size
* to the number of remaining bytes. Returns 0 on success,
* G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
* mechanism in the token does not match the mech argument. buf and
* *body_size are left unmodified on error.
*/
u32
g_verify_token_header(struct xdr_netobj *mech, int *body_size,
unsigned char **buf_in, int toksize)
{
unsigned char *buf = *buf_in;
int seqsize;
struct xdr_netobj toid;
int ret = 0;
if ((toksize-=1) < 0)
return G_BAD_TOK_HEADER;
if (*buf++ != 0x60)
return G_BAD_TOK_HEADER;
if ((seqsize = der_read_length(&buf, &toksize)) < 0)
return G_BAD_TOK_HEADER;
if (seqsize != toksize)
return G_BAD_TOK_HEADER;
if ((toksize-=1) < 0)
return G_BAD_TOK_HEADER;
if (*buf++ != 0x06)
return G_BAD_TOK_HEADER;
if ((toksize-=1) < 0)
return G_BAD_TOK_HEADER;
toid.len = *buf++;
if ((toksize-=toid.len) < 0)
return G_BAD_TOK_HEADER;
toid.data = buf;
buf+=toid.len;
if (! g_OID_equal(&toid, mech))
ret = G_WRONG_MECH;
/* G_WRONG_MECH is not returned immediately because it's more important
to return G_BAD_TOK_HEADER if the token header is in fact bad */
if ((toksize-=2) < 0)
return G_BAD_TOK_HEADER;
if (ret)
return ret;
*buf_in = buf;
*body_size = toksize;
return ret;
}
EXPORT_SYMBOL_GPL(g_verify_token_header);

@ -442,35 +442,6 @@ encryptor(struct scatterlist *sg, void *data)
return 0;
}
int
gss_encrypt_xdr_buf(struct crypto_sync_skcipher *tfm, struct xdr_buf *buf,
int offset, struct page **pages)
{
int ret;
struct encryptor_desc desc;
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
BUG_ON((buf->len - offset) % crypto_sync_skcipher_blocksize(tfm) != 0);
skcipher_request_set_sync_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
memset(desc.iv, 0, sizeof(desc.iv));
desc.req = req;
desc.pos = offset;
desc.outbuf = buf;
desc.pages = pages;
desc.fragno = 0;
desc.fraglen = 0;
sg_init_table(desc.infrags, 4);
sg_init_table(desc.outfrags, 4);
ret = xdr_process_buf(buf, offset, buf->len - offset, encryptor, &desc);
skcipher_request_zero(req);
return ret;
}
struct decryptor_desc {
u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
struct skcipher_request *req;
@ -525,32 +496,6 @@ decryptor(struct scatterlist *sg, void *data)
return 0;
}
int
gss_decrypt_xdr_buf(struct crypto_sync_skcipher *tfm, struct xdr_buf *buf,
int offset)
{
int ret;
struct decryptor_desc desc;
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
/* XXXJBF: */
BUG_ON((buf->len - offset) % crypto_sync_skcipher_blocksize(tfm) != 0);
skcipher_request_set_sync_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
memset(desc.iv, 0, sizeof(desc.iv));
desc.req = req;
desc.fragno = 0;
desc.fraglen = 0;
sg_init_table(desc.frags, 4);
ret = xdr_process_buf(buf, offset, buf->len - offset, decryptor, &desc);
skcipher_request_zero(req);
return ret;
}
/*
* This function makes the assumption that it was ultimately called
* from gss_wrap().

@ -172,13 +172,6 @@ u32 krb5_decrypt(struct crypto_sync_skcipher *key, void *iv, void *in,
int xdr_extend_head(struct xdr_buf *buf, unsigned int base,
unsigned int shiftlen);
int gss_encrypt_xdr_buf(struct crypto_sync_skcipher *tfm,
struct xdr_buf *outbuf, int offset,
struct page **pages);
int gss_decrypt_xdr_buf(struct crypto_sync_skcipher *tfm,
struct xdr_buf *inbuf, int offset);
u32 gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, struct page **pages);

@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/oid_registry.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/gss_asn1.h>
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_err.h>

@ -281,21 +281,7 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h
return rv;
}
/*
* This is the generic cache management routine for all
* the authentication caches.
* It checks the currency of a cache item and will (later)
* initiate an upcall to fill it if needed.
*
*
* Returns 0 if the cache_head can be used, or cache_puts it and returns
* -EAGAIN if upcall is pending and request has been queued
* -ETIMEDOUT if upcall failed or request could not be queue or
* upcall completed but item is still invalid (implying that
* the cache item has been replaced with a newer one).
* -ENOENT if cache entry was negative
*/
int cache_check(struct cache_detail *detail,
int cache_check_rcu(struct cache_detail *detail,
struct cache_head *h, struct cache_req *rqstp)
{
int rv;
@ -336,6 +322,31 @@ int cache_check(struct cache_detail *detail,
rv = -ETIMEDOUT;
}
}
return rv;
}
EXPORT_SYMBOL_GPL(cache_check_rcu);
/*
* This is the generic cache management routine for all
* the authentication caches.
* It checks the currency of a cache item and will (later)
* initiate an upcall to fill it if needed.
*
*
* Returns 0 if the cache_head can be used, or cache_puts it and returns
* -EAGAIN if upcall is pending and request has been queued
* -ETIMEDOUT if upcall failed or request could not be queue or
* upcall completed but item is still invalid (implying that
* the cache item has been replaced with a newer one).
* -ENOENT if cache entry was negative
*/
int cache_check(struct cache_detail *detail,
struct cache_head *h, struct cache_req *rqstp)
{
int rv;
rv = cache_check_rcu(detail, h, rqstp);
if (rv)
cache_put(h, detail);
return rv;
@ -1427,17 +1438,11 @@ static int c_show(struct seq_file *m, void *p)
seq_printf(m, "# expiry=%lld refcnt=%d flags=%lx\n",
convert_to_wallclock(cp->expiry_time),
kref_read(&cp->ref), cp->flags);
if (!cache_get_rcu(cp))
return 0;
if (cache_check(cd, cp, NULL))
/* cache_check does a cache_put on failure */
if (cache_check_rcu(cd, cp, NULL))
seq_puts(m, "# ");
else if (cache_is_expired(cd, cp))
seq_puts(m, "# ");
else {
if (cache_is_expired(cd, cp))
seq_puts(m, "# ");
cache_put(cp, cd);
}
return cd->cache_show(m, cd, cp);
}

@ -606,7 +606,8 @@ int svc_port_is_privileged(struct sockaddr *sin)
}
/*
* Make sure that we don't have too many active connections. If we have,
* Make sure that we don't have too many connections that have not yet
* demonstrated that they have access to the NFS server. If we have,
* something must be dropped. It's not clear what will happen if we allow
* "too many" connections, but when dealing with network-facing software,
* we have to code defensively. Here we do that by imposing hard limits.
@ -618,34 +619,26 @@ int svc_port_is_privileged(struct sockaddr *sin)
* The only somewhat efficient mechanism would be if drop old
* connections from the same IP first. But right now we don't even
* record the client IP in svc_sock.
*
* single-threaded services that expect a lot of clients will probably
* need to set sv_maxconn to override the default value which is based
* on the number of threads
*/
static void svc_check_conn_limits(struct svc_serv *serv)
{
unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn :
(serv->sv_nrthreads+3) * 20;
if (serv->sv_tmpcnt > limit) {
struct svc_xprt *xprt = NULL;
if (serv->sv_tmpcnt > XPT_MAX_TMP_CONN) {
struct svc_xprt *xprt = NULL, *xprti;
spin_lock_bh(&serv->sv_lock);
if (!list_empty(&serv->sv_tempsocks)) {
/* Try to help the admin */
net_notice_ratelimited("%s: too many open connections, consider increasing the %s\n",
serv->sv_name, serv->sv_maxconn ?
"max number of connections" :
"number of threads");
/*
* Always select the oldest connection. It's not fair,
* but so is life
* but nor is life.
*/
xprt = list_entry(serv->sv_tempsocks.prev,
struct svc_xprt,
xpt_list);
set_bit(XPT_CLOSE, &xprt->xpt_flags);
svc_xprt_get(xprt);
list_for_each_entry_reverse(xprti, &serv->sv_tempsocks,
xpt_list) {
if (!test_bit(XPT_PEER_VALID, &xprti->xpt_flags)) {
xprt = xprti;
set_bit(XPT_CLOSE, &xprt->xpt_flags);
svc_xprt_get(xprt);
break;
}
}
}
spin_unlock_bh(&serv->sv_lock);
@ -1038,7 +1031,8 @@ static void svc_delete_xprt(struct svc_xprt *xprt)
spin_lock_bh(&serv->sv_lock);
list_del_init(&xprt->xpt_list);
if (test_bit(XPT_TEMP, &xprt->xpt_flags))
if (test_bit(XPT_TEMP, &xprt->xpt_flags) &&
!test_bit(XPT_PEER_VALID, &xprt->xpt_flags))
serv->sv_tmpcnt--;
spin_unlock_bh(&serv->sv_lock);

@ -1083,9 +1083,6 @@ static void svc_tcp_fragment_received(struct svc_sock *svsk)
/* If we have more data, signal svc_xprt_enqueue() to try again */
svsk->sk_tcplen = 0;
svsk->sk_marker = xdr_zero;
smp_wmb();
tcp_set_rcvlowat(svsk->sk_sk, 1);
}
/**
@ -1175,17 +1172,10 @@ err_incomplete:
goto err_delete;
if (len == want)
svc_tcp_fragment_received(svsk);
else {
/* Avoid more ->sk_data_ready() calls until the rest
* of the message has arrived. This reduces service
* thread wake-ups on large incoming messages. */
tcp_set_rcvlowat(svsk->sk_sk,
svc_sock_reclen(svsk) - svsk->sk_tcplen);
else
trace_svcsock_tcp_recv_short(&svsk->sk_xprt,
svc_sock_reclen(svsk),
svsk->sk_tcplen - sizeof(rpc_fraghdr));
}
goto err_noclose;
error:
if (len != -EAGAIN)

@ -1097,6 +1097,12 @@ out_overflow:
* Checks that we have enough buffer space to encode 'nbytes' more
* bytes of data. If so, update the total xdr_buf length, and
* adjust the length of the current kvec.
*
* The returned pointer is valid only until the next call to
* xdr_reserve_space() or xdr_commit_encode() on @xdr. The current
* implementation of this API guarantees that space reserved for a
* four-byte data item remains valid until @xdr is destroyed, but
* that might not always be true in the future.
*/
__be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
{

@ -602,23 +602,6 @@ struct rpc_xprt *xprt_iter_get_helper(struct rpc_xprt_iter *xpi,
return ret;
}
/**
* xprt_iter_get_xprt - Returns the rpc_xprt pointed to by the cursor
* @xpi: pointer to rpc_xprt_iter
*
* Returns a reference to the struct rpc_xprt that is currently
* pointed to by the cursor.
*/
struct rpc_xprt *xprt_iter_get_xprt(struct rpc_xprt_iter *xpi)
{
struct rpc_xprt *xprt;
rcu_read_lock();
xprt = xprt_iter_get_helper(xpi, xprt_iter_ops(xpi)->xpi_xprt);
rcu_read_unlock();
return xprt;
}
/**
* xprt_iter_get_next - Returns the next rpc_xprt following the cursor
* @xpi: pointer to rpc_xprt_iter