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

Pull nfsd updates from Chuck Lever:

 - CB_NOTIFY support for NFSD's NFSv4.1 directory delegations

   The server used to recall a delegation as soon as the directory
   changed. NFSD now watches delegated directories through fsnotify and
   reports adds, removes, renames, and directory attribute changes,
   carrying the filehandle and attributes of the affected entry, so
   clients can keep their caches. Some of the NOTIFY4 flags come from
   RFC 8881bis (Jeff Layton)

 - Continued netlink work

   A new server-stats-get operation reports what /proc/net/rpc/nfsd
   publishes, plus NFSv4 callback counts, and SUNRPC now keeps its
   per-procedure call counts per network namespace, so a container sees
   its own numbers. nfsstat reads all of this over netlink, with a
   procfs fallback for older kernels (Jeff Layton)

 - Remove SUNRPC service thread pool mode selection

   Per node is the right choice on any host we run today, so the auto,
   global, and percpu modes have been removed. A single-node host still
   gets one pool. A multi-NUMA host now gets a pool per node.

   sunrpc.pool_mode accepts the old names but no longer selects
   anything.

 - Bug fixes, clean-ups, and small optimizations:
    - async COPY offload rework (Jeff Layton)
    - more use-after-free fixes in the NFSv4 state revocation paths
    - percpu counter contention removed from the reply cache and IO
      accounting
    - a long list of hardening fixes (Chris Mason)

Sincere thanks to all contributors, reviewers, testers, and bug
reporters who participated in the v7.3 NFSD development cycle.

* tag 'nfsd-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (182 commits)
  nfsd: export NFSv4 callback op stats via netlink
  nfsd: count NFSv4 callback operations per netns
  sunrpc: remove unused svc_version vs_count field
  nfsd: implement server-stats-get netlink handler
  sunrpc: use per-net counts in svc_seq_show()
  sunrpc: add per-netns per-procedure call counts to svc_stat
  NFSD: Document reply_cache_stats ABI
  NFSD: Eliminate percpu counter contention in IO byte accounting
  NFSD: Eliminate percpu counter contention in reply cache statistics
  NFSD: Eliminate percpu counter contention in DRC memory accounting
  NFSD: Fix off-by-one in DRC bucket pruning limit
  NFSD: Relocate NFSv4 "supported attributes" to new header
  NFSD: Relocate nfsd4_set_netaddr()
  NFSD: Relocate nfsd_user_namespace()
  NFSD: Move struct readdir_cd
  NFSD: Move the export.h include from nfsd.h to auth.c
  NFSD: Remove '#include "nfsd.h"' from fs/nfsd/cache.h
  NFSD: include "netns.h"
  NFSD: Explicitly include "stats.h"
  NFSD: Make "stats.h" self-contained
  ...
This commit is contained in:
Linus Torvalds
2026-08-20 12:39:56 -07:00
117 changed files with 5969 additions and 1736 deletions

View File

@@ -0,0 +1,38 @@
What: /proc/fs/nfsd/reply_cache_stats
Date: March 2013
KernelVersion: 3.10
Contact: linux-nfs@vger.kernel.org
Description:
Provides statistics for the NFS server duplicate reply
cache (DRC). The file contains one labeled field per
line. Each line has the form "field name:" followed by
whitespace and a decimal value.
Fields:
======================= ====== ==========================
max entries u32 Upper bound on cache size
num entries u32 Current entry count
hash buckets u32 Number of hash buckets
mem usage s64 Bytes consumed by the DRC
cache hits s64 Requests answered from cache
cache misses s64 Requests not found in cache
not cached s64 Idempotent requests that
bypass the cache
payload misses s64 XID matched but request
checksum did not
longest chain len u32 Longest hash chain observed
cachesize at longest u32 Cache size when longest
chain was recorded
======================= ====== ==========================
Counter fields (cache hits, cache misses, not cached,
payload misses, mem usage) are maintained with per-cpu
counters and may briefly show stale values under
concurrent load. There is no way to reset these
counters; consumers should compute rates by sampling
over time.
New fields may be appended in future kernels. Parsers
should match on field name, not line position.
Users: nfs-utils (https://git.linux-nfs.org/?p=steved/nfs-utils.git)

View File

@@ -7470,19 +7470,13 @@ Kernel parameters
sunrpc.pool_mode=
[NFS]
Control how the NFS server code allocates CPUs to
service thread pools. Depending on how many NICs
you have and where their interrupts are bound, this
option will affect which CPUs will do NFS serving.
Note: this parameter cannot be changed while the
NFS server is running.
auto the server chooses an appropriate mode
automatically using heuristics
global a single global pool contains all CPUs
percpu one pool for each CPU
pernode one pool for each NUMA node (equivalent
to global on non-NUMA machines)
Deprecated. The NFS server now always uses one
service thread pool per NUMA node (equivalent to a
single global pool on non-NUMA machines). All of
the previously accepted values (auto, global,
percpu, pernode) are still accepted for backward
compatibility but are ignored: the mode is always
pernode, and reads always return "pernode".
sunrpc.tcp_slot_table_entries=
sunrpc.udp_slot_table_entries=

View File

@@ -330,6 +330,91 @@ attribute-sets:
of which client holds the state. Intended for use after
all clients have been unexported from a given path,
enabling the underlying filesystem to be unmounted.
-
name: server-proc-entry
attributes:
-
name: op
type: u32
-
name: count
type: u64
-
name: pad
type: pad
-
name: server-stats
attributes:
-
name: rc-hits
type: u64
-
name: rc-misses
type: u64
-
name: rc-nocache
type: u64
-
name: pad
type: pad
-
name: fh-stale
type: u64
-
name: io-read
type: u64
-
name: io-write
type: u64
-
name: netcnt
type: u32
-
name: netudpcnt
type: u32
-
name: nettcpcnt
type: u32
-
name: nettcpconn
type: u32
-
name: rpccnt
type: u32
-
name: rpcbadfmt
type: u32
-
name: rpcbadauth
type: u32
-
name: rpcbadclnt
type: u32
-
name: proc2-ops
type: nest
nested-attributes: server-proc-entry
multi-attr: true
-
name: proc3-ops
type: nest
nested-attributes: server-proc-entry
multi-attr: true
-
name: proc4-ops
type: nest
nested-attributes: server-proc-entry
multi-attr: true
-
name: proc4ops-ops
type: nest
nested-attributes: server-proc-entry
multi-attr: true
-
name: proc4cb-ops
type: nest
nested-attributes: server-proc-entry
multi-attr: true
operations:
list:
@@ -516,6 +601,32 @@ operations:
request:
attributes:
- path
-
name: server-stats-get
doc: dump NFS server statistics
attribute-set: server-stats
dump:
reply:
attributes:
- rc-hits
- rc-misses
- rc-nocache
- fh-stale
- io-read
- io-write
- netcnt
- netudpcnt
- nettcpcnt
- nettcpconn
- rpccnt
- rpcbadfmt
- rpcbadauth
- rpcbadclnt
- proc2-ops
- proc3-ops
- proc4-ops
- proc4ops-ops
- proc4cb-ops
mcast-groups:
list:

View File

@@ -45,19 +45,162 @@ pragma header nfs4;
/*
* Basic typedefs for RFC 1832 data type definitions
*/
typedef hyper int64_t;
typedef unsigned int uint32_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef hyper int64_t;
typedef unsigned hyper uint64_t;
const NFS4_VERIFIER_SIZE = 8;
const NFS4_FHSIZE = 128;
enum nfsstat4 {
NFS4_OK = 0, /* everything is okay */
NFS4ERR_PERM = 1, /* caller not privileged */
NFS4ERR_NOENT = 2, /* no such file/directory */
NFS4ERR_IO = 5, /* hard I/O error */
NFS4ERR_NXIO = 6, /* no such device */
NFS4ERR_ACCESS = 13, /* access denied */
NFS4ERR_EXIST = 17, /* file already exists */
NFS4ERR_XDEV = 18, /* different filesystems */
/*
* Please do not allocate value 19; it was used in NFSv3
* and we do not want a value in NFSv3 to have a different
* meaning in NFSv4.x.
*/
NFS4ERR_NOTDIR = 20, /* should be a directory */
NFS4ERR_ISDIR = 21, /* should not be directory */
NFS4ERR_INVAL = 22, /* invalid argument */
NFS4ERR_FBIG = 27, /* file exceeds server max */
NFS4ERR_NOSPC = 28, /* no space on filesystem */
NFS4ERR_ROFS = 30, /* read-only filesystem */
NFS4ERR_MLINK = 31, /* too many hard links */
NFS4ERR_NAMETOOLONG = 63, /* name exceeds server max */
NFS4ERR_NOTEMPTY = 66, /* directory not empty */
NFS4ERR_DQUOT = 69, /* hard quota limit reached*/
NFS4ERR_STALE = 70, /* file no longer exists */
NFS4ERR_BADHANDLE = 10001,/* Illegal filehandle */
NFS4ERR_BAD_COOKIE = 10003,/* READDIR cookie is stale */
NFS4ERR_NOTSUPP = 10004,/* operation not supported */
NFS4ERR_TOOSMALL = 10005,/* response limit exceeded */
NFS4ERR_SERVERFAULT = 10006,/* undefined server error */
NFS4ERR_BADTYPE = 10007,/* type invalid for CREATE */
NFS4ERR_DELAY = 10008,/* file "busy" - retry */
NFS4ERR_SAME = 10009,/* nverify says attrs same */
NFS4ERR_DENIED = 10010,/* lock unavailable */
NFS4ERR_EXPIRED = 10011,/* lock lease expired */
NFS4ERR_LOCKED = 10012,/* I/O failed due to lock */
NFS4ERR_GRACE = 10013,/* in grace period */
NFS4ERR_FHEXPIRED = 10014,/* filehandle expired */
NFS4ERR_SHARE_DENIED = 10015,/* share reserve denied */
NFS4ERR_WRONGSEC = 10016,/* wrong security flavor */
NFS4ERR_CLID_INUSE = 10017,/* clientid in use */
/* NFS4ERR_RESOURCE is not a valid error in NFSv4.1 */
NFS4ERR_RESOURCE = 10018,/* resource exhaustion */
NFS4ERR_MOVED = 10019,/* filesystem relocated */
NFS4ERR_NOFILEHANDLE = 10020,/* current FH is not set */
NFS4ERR_MINOR_VERS_MISMATCH= 10021,/* minor vers not supp */
NFS4ERR_STALE_CLIENTID = 10022,/* server has rebooted */
NFS4ERR_STALE_STATEID = 10023,/* server has rebooted */
NFS4ERR_OLD_STATEID = 10024,/* state is out of sync */
NFS4ERR_BAD_STATEID = 10025,/* incorrect stateid */
NFS4ERR_BAD_SEQID = 10026,/* request is out of seq. */
NFS4ERR_NOT_SAME = 10027,/* verify - attrs not same */
NFS4ERR_LOCK_RANGE = 10028,/* overlapping lock range */
NFS4ERR_SYMLINK = 10029,/* should be file/directory*/
NFS4ERR_RESTOREFH = 10030,/* no saved filehandle */
NFS4ERR_LEASE_MOVED = 10031,/* some filesystem moved */
NFS4ERR_ATTRNOTSUPP = 10032,/* recommended attr not sup*/
NFS4ERR_NO_GRACE = 10033,/* reclaim outside of grace*/
NFS4ERR_RECLAIM_BAD = 10034,/* reclaim error at server */
NFS4ERR_RECLAIM_CONFLICT= 10035,/* conflict on reclaim */
NFS4ERR_BADXDR = 10036,/* XDR decode failed */
NFS4ERR_LOCKS_HELD = 10037,/* file locks held at CLOSE*/
NFS4ERR_OPENMODE = 10038,/* conflict in OPEN and I/O*/
NFS4ERR_BADOWNER = 10039,/* owner translation bad */
NFS4ERR_BADCHAR = 10040,/* utf-8 char not supported*/
NFS4ERR_BADNAME = 10041,/* name not supported */
NFS4ERR_BAD_RANGE = 10042,/* lock range not supported*/
NFS4ERR_LOCK_NOTSUPP = 10043,/* no atomic up/downgrade */
NFS4ERR_OP_ILLEGAL = 10044,/* undefined operation */
NFS4ERR_DEADLOCK = 10045,/* file locking deadlock */
NFS4ERR_FILE_OPEN = 10046,/* open file blocks op. */
NFS4ERR_ADMIN_REVOKED = 10047,/* lockowner state revoked */
NFS4ERR_CB_PATH_DOWN = 10048,/* callback path down */
/* NFSv4.1 errors start here. */
NFS4ERR_BADIOMODE = 10049,
NFS4ERR_BADLAYOUT = 10050,
NFS4ERR_BAD_SESSION_DIGEST = 10051,
NFS4ERR_BADSESSION = 10052,
NFS4ERR_BADSLOT = 10053,
NFS4ERR_COMPLETE_ALREADY = 10054,
NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
NFS4ERR_DELEG_ALREADY_WANTED = 10056,
NFS4ERR_BACK_CHAN_BUSY = 10057,/*backchan reqs outstanding*/
NFS4ERR_LAYOUTTRYLATER = 10058,
NFS4ERR_LAYOUTUNAVAILABLE = 10059,
NFS4ERR_NOMATCHING_LAYOUT = 10060,
NFS4ERR_RECALLCONFLICT = 10061,
NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
NFS4ERR_SEQ_MISORDERED = 10063,/* unexpected seq.ID in req*/
NFS4ERR_SEQUENCE_POS = 10064,/* [CB_]SEQ. op not 1st op */
NFS4ERR_REQ_TOO_BIG = 10065,/* request too big */
NFS4ERR_REP_TOO_BIG = 10066,/* reply too big */
NFS4ERR_REP_TOO_BIG_TO_CACHE =10067,/* rep. not all cached*/
NFS4ERR_RETRY_UNCACHED_REP =10068,/* retry & rep. uncached*/
NFS4ERR_UNSAFE_COMPOUND =10069,/* retry/recovery too hard */
NFS4ERR_TOO_MANY_OPS = 10070,/*too many ops in [CB_]COMP*/
NFS4ERR_OP_NOT_IN_SESSION =10071,/* op needs [CB_]SEQ. op */
NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
/* Error 10073 is unused. */
NFS4ERR_CLIENTID_BUSY = 10074,/* clientid has state */
NFS4ERR_PNFS_IO_HOLE = 10075,/* IO to _SPARSE file hole */
NFS4ERR_SEQ_FALSE_RETRY= 10076,/* Retry != original req. */
NFS4ERR_BAD_HIGH_SLOT = 10077,/* req has bad highest_slot*/
NFS4ERR_DEADSESSION = 10078,/*new req sent to dead sess*/
NFS4ERR_ENCR_ALG_UNSUPP= 10079,/* encr alg. not supp. */
NFS4ERR_PNFS_NO_LAYOUT = 10080,/* I/O without a layout */
NFS4ERR_NOT_ONLY_OP = 10081,/* addl ops not allowed */
NFS4ERR_WRONG_CRED = 10082,/* op done by wrong cred */
NFS4ERR_WRONG_TYPE = 10083,/* op on wrong type object */
NFS4ERR_DIRDELEG_UNAVAIL=10084,/* delegation not avail. */
NFS4ERR_REJECT_DELEG = 10085,/* cb rejected delegation */
NFS4ERR_RETURNCONFLICT = 10086,/* layout get before return*/
NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */
NFS4ERR_PARTNER_NOTSUPP = 10088,
NFS4ERR_PARTNER_NO_AUTH = 10089,
NFS4ERR_UNION_NOTSUPP = 10090,
NFS4ERR_OFFLOAD_DENIED = 10091,
NFS4ERR_WRONG_LFS = 10092,
NFS4ERR_BADLABEL = 10093,
NFS4ERR_OFFLOAD_NO_REQS = 10094,
NFS4ERR_NOXATTR = 10095,
NFS4ERR_XATTR2BIG = 10096
};
/*
* Basic data types
*/
typedef opaque attrlist4<>;
typedef uint32_t bitmap4<>;
typedef opaque verifier4[NFS4_VERIFIER_SIZE];
typedef uint64_t nfs_cookie4;
typedef opaque nfs_fh4<NFS4_FHSIZE>;
typedef opaque utf8string<>;
typedef utf8string utf8str_cis;
typedef utf8string utf8str_cs;
typedef utf8string utf8str_mixed;
typedef utf8str_cs component4;
typedef utf8str_cs linktext4;
typedef component4 pathname4<>;
/*
* Timeval
*/
@@ -66,6 +209,21 @@ struct nfstime4 {
uint32_t nseconds;
};
/*
* File attribute container
*/
struct fattr4 {
bitmap4 attrmask;
attrlist4 attr_vals;
};
/*
* Stateid
*/
struct stateid4 {
uint32_t seqid;
opaque other[12];
};
/*
* The following content was extracted from draft-ietf-nfsv4-delstid
@@ -245,3 +403,99 @@ const FATTR4_ACL_TRUEFORM = 89;
const FATTR4_ACL_TRUEFORM_SCOPE = 90;
const FATTR4_POSIX_DEFAULT_ACL = 91;
const FATTR4_POSIX_ACCESS_ACL = 92;
/*
* Directory notification types.
*/
enum notify_type4 {
NOTIFY4_CHANGE_CHILD_ATTRS = 0,
NOTIFY4_CHANGE_DIR_ATTRS = 1,
NOTIFY4_REMOVE_ENTRY = 2,
NOTIFY4_ADD_ENTRY = 3,
NOTIFY4_RENAME_ENTRY = 4,
NOTIFY4_CHANGE_COOKIE_VERIFIER = 5,
/* Proposed in RFC8881bis */
NOTIFY4_GFLAG_EXTEND = 6,
NOTIFY4_AUFLAG_VALID = 7,
NOTIFY4_AUFLAG_USER = 8,
NOTIFY4_AUFLAG_GROUP = 9,
NOTIFY4_AUFLAG_OTHER = 10,
NOTIFY4_CHANGE_AUTH = 11,
NOTIFY4_CFLAG_ORDER = 12,
NOTIFY4_AUFLAG_GANOW = 13,
NOTIFY4_AUFLAG_GALATER = 14,
NOTIFY4_CHANGE_GA = 15,
NOTIFY4_CHANGE_AMASK = 16
};
/* Changed entry information. */
struct notify_entry4 {
component4 ne_file;
fattr4 ne_attrs;
};
/* Previous entry information */
struct prev_entry4 {
notify_entry4 pe_prev_entry;
/* what READDIR returned for this entry */
nfs_cookie4 pe_prev_entry_cookie;
};
struct notify_remove4 {
notify_entry4 nrm_old_entry;
nfs_cookie4 nrm_old_entry_cookie;
};
pragma public notify_remove4;
struct notify_add4 {
/*
* Information on object
* possibly renamed over.
*/
notify_remove4 nad_old_entry<1>;
notify_entry4 nad_new_entry;
/* what READDIR would have returned for this entry */
nfs_cookie4 nad_new_entry_cookie<1>;
prev_entry4 nad_prev_entry<1>;
bool nad_last_entry;
};
pragma public notify_add4;
struct notify_attr4 {
notify_entry4 na_changed_entry;
};
pragma public notify_attr4;
struct notify_rename4 {
notify_remove4 nrn_old_entry;
notify_add4 nrn_new_entry;
};
pragma public notify_rename4;
struct notify_verifier4 {
verifier4 nv_old_cookieverf;
verifier4 nv_new_cookieverf;
};
/*
* Objects of type notify_<>4 and
* notify_device_<>4 are encoded in this.
*/
typedef opaque notifylist4<>;
struct notify4 {
/* composed from notify_type4 or notify_deviceid_type4 */
bitmap4 notify_mask;
notifylist4 notify_vals;
};
struct CB_NOTIFY4args {
stateid4 cna_stateid;
nfs_fh4 cna_fh;
notify4 cna_changes<>;
};
pragma public CB_NOTIFY4args;
struct CB_NOTIFY4res {
nfsstat4 cnr_status;
};

View File

@@ -314,7 +314,7 @@ void nsm_release(struct nsm_handle *nsm);
* This is used in garbage collection and resource reclaim
* A return value != 0 means destroy the lock/block/share
*/
typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
typedef int (*nlm_host_match_fn_t)(void *owner, struct nlm_host *ref);
/*
* Server-side lock handling

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x
// XDR specification modification time: Thu Apr 23 10:56:34 2026
// XDR specification modification time: Mon Jun 29 20:42:29 2026
#include <linux/sunrpc/svc.h>
@@ -271,20 +271,6 @@ xdrgen_decode_nlm_notifyargs(struct xdr_stream *xdr, struct nlm_notifyargs *ptr)
return true;
}
/**
* nlm_svc_decode_void - Decode a void argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_decode_void(xdr);
}
/**
* nlm_svc_decode_nlm_testargs - Decode a nlm_testargs argument
* @rqstp: RPC transaction context
@@ -651,20 +637,6 @@ xdrgen_encode_nlm_notifyargs(struct xdr_stream *xdr, const struct nlm_notifyargs
return true;
}
/**
* nlm_svc_encode_void - Encode a void result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_encode_void(xdr);
}
/**
* nlm_svc_encode_nlm_testres - Encode a nlm_testres result
* @rqstp: RPC transaction context

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x */
/* XDR specification modification time: Thu Apr 23 10:56:34 2026 */
/* XDR specification modification time: Mon Jun 29 20:42:29 2026 */
#ifndef _LINUX_XDRGEN_NLM3_DECL_H
#define _LINUX_XDRGEN_NLM3_DECL_H
@@ -13,7 +13,6 @@
#include <linux/sunrpc/xdrgen/_builtins.h>
#include <linux/sunrpc/xdrgen/nlm3.h>
bool nlm_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
@@ -24,7 +23,6 @@ bool nlm_svc_decode_nlm_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xd
bool nlm_svc_decode_nlm_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_nlm_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr);

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x
// XDR specification modification time: Thu Dec 25 13:10:19 2025
// XDR specification modification time: Mon Jun 29 20:42:29 2026
#include <linux/sunrpc/svc.h>
@@ -20,6 +20,16 @@ xdrgen_decode_fsh4_mode(struct xdr_stream *xdr, fsh4_mode *ptr)
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case fsm_DN:
case fsm_DR:
case fsm_DW:
case fsm_DRW:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -31,6 +41,16 @@ xdrgen_decode_fsh4_access(struct xdr_stream *xdr, fsh4_access *ptr)
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case fsa_NONE:
case fsa_R:
case fsa_W:
case fsa_RW:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -62,7 +82,30 @@ xdrgen_decode_int32(struct xdr_stream *xdr, int32 *ptr)
static bool __maybe_unused
xdrgen_decode_nlm4_stats(struct xdr_stream *xdr, nlm4_stats *ptr)
{
return xdr_stream_decode_be32(xdr, ptr) == 0;
__be32 raw;
u32 val;
if (xdr_stream_decode_be32(xdr, &raw) < 0)
return false;
val = be32_to_cpu(raw);
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case NLM4_GRANTED:
case NLM4_DENIED:
case NLM4_DENIED_NOLOCKS:
case NLM4_BLOCKED:
case NLM4_DENIED_GRACE_PERIOD:
case NLM4_DEADLCK:
case NLM4_ROFS:
case NLM4_STALE_FH:
case NLM4_FBIG:
case NLM4_FAILED:
break;
default:
return false;
}
*ptr = raw;
return true;
}
static bool __maybe_unused
@@ -257,20 +300,6 @@ xdrgen_decode_nlm4_notifyargs(struct xdr_stream *xdr, struct nlm4_notifyargs *pt
return true;
}
/**
* nlm4_svc_decode_void - Decode a void argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_decode_void(xdr);
}
/**
* nlm4_svc_decode_nlm4_testargs - Decode a nlm4_testargs argument
* @rqstp: RPC transaction context
@@ -661,20 +690,6 @@ xdrgen_encode_nlm4_notifyargs(struct xdr_stream *xdr, const struct nlm4_notifyar
return true;
}
/**
* nlm4_svc_encode_void - Encode a void result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm4_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_encode_void(xdr);
}
/**
* nlm4_svc_encode_nlm4_testres - Encode a nlm4_testres result
* @rqstp: RPC transaction context

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x */
/* XDR specification modification time: Thu Dec 25 13:10:19 2025 */
/* XDR specification modification time: Mon Jun 29 20:42:29 2026 */
#ifndef _LINUX_XDRGEN_NLM4_DECL_H
#define _LINUX_XDRGEN_NLM4_DECL_H
@@ -13,7 +13,6 @@
#include <linux/sunrpc/xdrgen/_builtins.h>
#include <linux/sunrpc/xdrgen/nlm4.h>
bool nlm4_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
@@ -24,7 +23,6 @@ bool nlm4_svc_decode_nlm4_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *
bool nlm4_svc_decode_nlm4_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_nlm4_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr);

View File

@@ -8,9 +8,14 @@
#ifndef _LOCKD_SHARE_H
#define _LOCKD_SHARE_H
#include <linux/bits.h>
/* Synthetic svid for lockowner lookup during share operations */
#define LOCKD_SHARE_SVID (~(u32)0)
/* One bit per (access, deny) pair; index = (access << 2) | deny */
#define LOCKD_FSH_BIT(a, d) BIT(((a) << 2) | (d))
/*
* DOS share for a specific file
*/
@@ -21,12 +26,13 @@ struct lockd_share {
struct xdr_netobj s_owner; /* owner handle */
u32 s_access; /* access mode */
u32 s_mode; /* deny mode */
u16 s_access_deny_bmap; /* held (access, deny) pairs */
};
__be32 nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh, u32 access, u32 mode);
__be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh);
struct xdr_netobj *oh, u32 access, u32 mode);
void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *,
nlm_host_match_fn_t);

View File

@@ -47,7 +47,7 @@
static struct svc_program nlmsvc_program;
const struct nlmsvc_binding *nlmsvc_ops;
const struct nlmsvc_binding __rcu *nlmsvc_ops;
EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
@@ -142,7 +142,7 @@ lockd(void *vrqstp)
nlmsvc_retry_blocked(rqstp);
svc_recv(rqstp, 0);
}
if (nlmsvc_ops)
if (rcu_access_pointer(nlmsvc_ops))
nlmsvc_invalidate_all();
nlm_shutdown_hosts();
cancel_delayed_work_sync(&ln->grace_period_end);

View File

@@ -128,7 +128,7 @@ nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
struct nlm_host *host;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return NULL;
host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
if (!host)
@@ -872,7 +872,8 @@ static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp)
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
host = nlmsvc_lookup_host(rqstp, argp->xdrgen.alock.caller_name.data,
argp->xdrgen.alock.caller_name.len);
if (!host)
return rpc_system_err;
@@ -894,7 +895,7 @@ static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
{
struct nlm4_res_wrapper *argp = rqstp->rq_argp;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return rpc_success;
if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
@@ -1078,7 +1079,9 @@ static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp)
if (resp->xdrgen.stat)
goto out;
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh);
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh,
argp->xdrgen.share.access,
argp->xdrgen.share.mode);
nlmsvc_release_lockowner(lock);
@@ -1164,8 +1167,8 @@ static __be32 nlm4svc_proc_free_all(struct svc_rqst *rqstp)
static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_NULL] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = XDR_void,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1225,7 +1228,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_TEST_MSG] = {
.pc_func = nlm4svc_proc_test_msg,
.pc_decode = nlm4_svc_decode_nlm4_testargs,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1235,7 +1238,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_LOCK_MSG] = {
.pc_func = nlm4svc_proc_lock_msg,
.pc_decode = nlm4_svc_decode_nlm4_lockargs,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_lockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1245,7 +1248,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_CANCEL_MSG] = {
.pc_func = nlm4svc_proc_cancel_msg,
.pc_decode = nlm4_svc_decode_nlm4_cancargs,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_cancargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1255,7 +1258,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_UNLOCK_MSG] = {
.pc_func = nlm4svc_proc_unlock_msg,
.pc_decode = nlm4_svc_decode_nlm4_unlockargs,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_unlockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1265,7 +1268,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_GRANTED_MSG] = {
.pc_func = nlm4svc_proc_granted_msg,
.pc_decode = nlm4_svc_decode_nlm4_testargs,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1275,7 +1278,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_TEST_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_testres,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_testres),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1285,7 +1288,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_LOCK_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1295,7 +1298,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_CANCEL_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1305,7 +1308,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_UNLOCK_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1315,7 +1318,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_GRANTED_RES] = {
.pc_func = nlm4svc_proc_granted_res,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1325,7 +1328,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_SM_NOTIFY] = {
.pc_func = nlm4svc_proc_sm_notify,
.pc_decode = nlm4_svc_decode_nlm4_notifyargs,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_notifyargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1334,8 +1337,8 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
},
[17] = {
.pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1344,8 +1347,8 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
},
[18] = {
.pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1354,8 +1357,8 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
},
[19] = {
.pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1395,7 +1398,7 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_FREE_ALL] = {
.pc_func = nlm4svc_proc_free_all,
.pc_decode = nlm4_svc_decode_nlm4_notify,
.pc_encode = nlm4_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_notify_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1420,14 +1423,10 @@ union nlm4svc_xdrstore {
struct nlm4_shareres_wrapper shareres;
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nlm4svc_call_counters[ARRAY_SIZE(nlm4svc_procedures)]);
const struct svc_version nlmsvc_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nlm4svc_procedures),
.vs_proc = nlm4svc_procedures,
.vs_count = nlm4svc_call_counters,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = sizeof(union nlm4svc_xdrstore),
};

View File

@@ -47,40 +47,6 @@ static const struct rpc_call_ops nlmsvc_grant_ops;
static LIST_HEAD(nlm_blocked);
static DEFINE_SPINLOCK(nlm_blocked_lock);
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie)
{
/*
* We can get away with a static buffer because this is only called
* from lockd, which is single-threaded.
*/
static char buf[2*NLM_MAXCOOKIELEN+1];
unsigned int i, len = sizeof(buf);
char *p = buf;
len--; /* allow for trailing \0 */
if (len < 3)
return "???";
for (i = 0 ; i < cookie->len ; i++) {
if (len < 2) {
strcpy(p-3, "...");
break;
}
sprintf(p, "%02x", cookie->data[i]);
p += 2;
len -= 2;
}
*p = '\0';
return buf;
}
#else
static inline const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie)
{
return "???";
}
#endif
/*
* Insert a blocked lock into the global list
*/
@@ -155,11 +121,12 @@ nlmsvc_lookup_block(struct nlm_file *file, struct lockd_lock *lock)
spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
fl = &block->b_call->a_args.lock.fl;
dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%*phN\n",
block->b_file, fl->c.flc_pid,
(long long)fl->fl_start,
(long long)fl->fl_end, fl->c.flc_type,
nlmdbg_cookie2a(&block->b_call->a_args.cookie));
block->b_call->a_args.cookie.len,
block->b_call->a_args.cookie.data);
if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
kref_get(&block->b_count);
spin_unlock(&nlm_blocked_lock);
@@ -198,7 +165,8 @@ nlmsvc_find_block(struct lockd_cookie *cookie)
return NULL;
found:
dprintk("nlmsvc_find_block(%s): block=%p\n", nlmdbg_cookie2a(cookie), block);
dprintk("nlmsvc_find_block(%*phN): block=%p\n",
cookie->len, cookie->data, block);
kref_get(&block->b_count);
spin_unlock(&nlm_blocked_lock);
return block;

View File

@@ -133,7 +133,7 @@ nlm3svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
struct nlm_host *host;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return NULL;
host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
if (!host)
@@ -924,7 +924,7 @@ static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
{
struct nlm_res_wrapper *argp = rqstp->rq_argp;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return rpc_success;
if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
@@ -1098,7 +1098,9 @@ static __be32 nlmsvc_proc_unshare(struct svc_rqst *rqstp)
if (resp->xdrgen.stat)
goto out;
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh);
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh,
argp->xdrgen.share.access,
argp->xdrgen.share.mode);
nlmsvc_release_lockowner(lock);
@@ -1174,8 +1176,8 @@ static __be32 nlmsvc_proc_free_all(struct svc_rqst *rqstp)
static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_NULL] = {
.pc_func = nlmsvc_proc_null,
.pc_decode = nlm_svc_decode_void,
.pc_encode = nlm_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = XDR_void,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1235,7 +1237,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_TEST_MSG] = {
.pc_func = nlmsvc_proc_test_msg,
.pc_decode = nlm_svc_decode_nlm_testargs,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1245,7 +1247,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_LOCK_MSG] = {
.pc_func = nlmsvc_proc_lock_msg,
.pc_decode = nlm_svc_decode_nlm_lockargs,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_lockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1255,7 +1257,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_CANCEL_MSG] = {
.pc_func = nlmsvc_proc_cancel_msg,
.pc_decode = nlm_svc_decode_nlm_cancargs,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_cancargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1265,7 +1267,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_UNLOCK_MSG] = {
.pc_func = nlmsvc_proc_unlock_msg,
.pc_decode = nlm_svc_decode_nlm_unlockargs,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_unlockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1275,7 +1277,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_GRANTED_MSG] = {
.pc_func = nlmsvc_proc_granted_msg,
.pc_decode = nlm_svc_decode_nlm_testargs,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1285,7 +1287,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_TEST_RES] = {
.pc_func = nlmsvc_proc_null,
.pc_decode = nlm_svc_decode_nlm_testres,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_testres),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1295,7 +1297,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_LOCK_RES] = {
.pc_func = nlmsvc_proc_null,
.pc_decode = nlm_svc_decode_nlm_res,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_res),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1305,7 +1307,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_CANCEL_RES] = {
.pc_func = nlmsvc_proc_null,
.pc_decode = nlm_svc_decode_nlm_res,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_res),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1315,7 +1317,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_UNLOCK_RES] = {
.pc_func = nlmsvc_proc_null,
.pc_decode = nlm_svc_decode_nlm_res,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_res),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1325,7 +1327,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_GRANTED_RES] = {
.pc_func = nlmsvc_proc_granted_res,
.pc_decode = nlm_svc_decode_nlm_res,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_res_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1335,7 +1337,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_SM_NOTIFY] = {
.pc_func = nlmsvc_proc_sm_notify,
.pc_decode = nlm_svc_decode_nlm_notifyargs,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_notifyargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1344,8 +1346,8 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
},
[17] = {
.pc_func = nlmsvc_proc_unused,
.pc_decode = nlm_svc_decode_void,
.pc_encode = nlm_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1354,8 +1356,8 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
},
[18] = {
.pc_func = nlmsvc_proc_unused,
.pc_decode = nlm_svc_decode_void,
.pc_encode = nlm_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1364,8 +1366,8 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
},
[19] = {
.pc_func = nlmsvc_proc_unused,
.pc_decode = nlm_svc_decode_void,
.pc_encode = nlm_svc_encode_void,
.pc_decode = xdrgen_svc_decode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1405,7 +1407,7 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
[NLM_FREE_ALL] = {
.pc_func = nlmsvc_proc_free_all,
.pc_decode = nlm_svc_decode_nlm_notify,
.pc_encode = nlm_svc_encode_void,
.pc_encode = xdrgen_svc_encode_void,
.pc_argsize = sizeof(struct nlm_notify_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
@@ -1434,25 +1436,18 @@ union nlmsvc_xdrstore {
* NLMv1 defines only procedures 1 - 15. Linux lockd also implements
* procedures 0 (NULL) and 16 (SM_NOTIFY).
*/
static DEFINE_PER_CPU_ALIGNED(unsigned long, nlm1svc_call_counters[17]);
const struct svc_version nlmsvc_version1 = {
.vs_vers = 1,
.vs_nproc = 17,
.vs_proc = nlmsvc_procedures,
.vs_count = nlm1svc_call_counters,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = sizeof(union nlmsvc_xdrstore),
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nlm3svc_call_counters[ARRAY_SIZE(nlmsvc_procedures)]);
const struct svc_version nlmsvc_version3 = {
.vs_vers = 3,
.vs_nproc = ARRAY_SIZE(nlmsvc_procedures),
.vs_proc = nlmsvc_procedures,
.vs_count = nlm3svc_call_counters,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = sizeof(union nlmsvc_xdrstore),
};

View File

@@ -25,6 +25,25 @@ nlm_cmp_owner(struct lockd_share *share, struct xdr_netobj *oh)
&& !memcmp(share->s_owner.data, oh->data, oh->len);
}
/*
* Recompute s_access / s_mode as the union of every (access, deny) pair
* whose bit is currently set in s_access_deny_bmap.
*/
static void nlm_recompute_share(struct lockd_share *share)
{
u32 new_access = 0, new_mode = 0;
unsigned int i;
for (i = 0; i < 16; i++) {
if (share->s_access_deny_bmap & BIT(i)) {
new_access |= i >> 2;
new_mode |= i & 3;
}
}
share->s_access = new_access;
share->s_mode = new_mode;
}
/**
* nlmsvc_share_file - create a share
* @host: Network client peer
@@ -64,12 +83,13 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
share->s_host = host;
share->s_owner.data = ohdata;
share->s_owner.len = oh->len;
share->s_access_deny_bmap = 0;
share->s_next = file->f_shares;
file->f_shares = share;
update:
share->s_access = access;
share->s_mode = mode;
share->s_access_deny_bmap |= LOCKD_FSH_BIT(access, mode);
nlm_recompute_share(share);
return nlm_granted;
}
@@ -78,12 +98,14 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
* @host: Network client peer
* @file: File to be unshared
* @oh: Share owner handle
* @access: Access mode of the SHARE being released
* @mode: Deny mode of the SHARE being released
*
* Returns an NLM status code.
*/
__be32
nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh)
struct xdr_netobj *oh, u32 access, u32 mode)
{
struct lockd_share *share, **shpp;
@@ -93,8 +115,12 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
for (shpp = &file->f_shares; (share = *shpp) != NULL;
shpp = &share->s_next) {
if (share->s_host == host && nlm_cmp_owner(share, oh)) {
*shpp = share->s_next;
kfree(share);
share->s_access_deny_bmap &= ~LOCKD_FSH_BIT(access, mode);
nlm_recompute_share(share);
if (!share->s_access_deny_bmap) {
*shpp = share->s_next;
kfree(share);
}
return nlm_granted;
}
}

View File

@@ -90,22 +90,35 @@ int lock_to_openmode(struct file_lock *lock)
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
struct nlm_file *file, int mode)
{
const struct nlmsvc_binding *ops;
__be32 nlmerr = nlm__int__failed;
__be32 deferred = 0;
int error;
int m;
rcu_read_lock();
ops = rcu_dereference(nlmsvc_ops);
if (!ops || !try_module_get(ops->owner)) {
rcu_read_unlock();
return nlm__int__failed;
}
rcu_read_unlock();
for (m = O_RDONLY; m <= O_WRONLY; m++) {
struct file **fp = &file->f_file[m];
if (mode != O_RDWR && mode != m)
continue;
if (*fp)
if (*fp) {
module_put(ops->owner);
return nlm_granted;
}
error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m);
if (!error)
error = ops->fopen(rqstp, &file->f_handle, fp, m);
if (!error) {
module_put(ops->owner);
return nlm_granted;
}
dprintk("lockd: open failed (errno %d)\n", error);
switch (error) {
@@ -122,6 +135,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
}
}
module_put(ops->owner);
return deferred ? deferred : nlmerr;
}
@@ -185,6 +199,33 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
goto out_unlock;
}
/*
* Release the struct file references held by a nlm_file.
*/
static void nlm_release_files(struct nlm_file *file)
{
const struct nlmsvc_binding *ops;
bool have_ops;
rcu_read_lock();
ops = rcu_dereference(nlmsvc_ops);
have_ops = ops && try_module_get(ops->owner);
rcu_read_unlock();
if (have_ops) {
if (file->f_file[O_RDONLY])
ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
ops->fclose(file->f_file[O_WRONLY]);
module_put(ops->owner);
} else {
if (file->f_file[O_RDONLY])
fput(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
fput(file->f_file[O_WRONLY]);
}
}
/*
* Delete a file after having released all locks, blocks and shares
*/
@@ -194,10 +235,7 @@ nlm_delete_file(struct nlm_file *file)
nlm_debug_print_file("closing file", file);
if (!hlist_unhashed(&file->f_list)) {
hlist_del(&file->f_list);
if (file->f_file[O_RDONLY])
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
nlm_release_files(file);
kfree(file);
} else {
printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
@@ -312,12 +350,10 @@ nlm_file_inuse(struct nlm_file *file)
return 0;
}
static void nlm_close_files(struct nlm_file *file)
static void nlm_file_release(struct nlm_file *file)
{
if (file->f_file[O_RDONLY])
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
if (!nlm_file_inuse(file))
nlm_delete_file(file);
}
/*
@@ -327,32 +363,41 @@ static int
nlm_traverse_files(void *data, nlm_host_match_fn_t match,
int (*is_failover_file)(void *data, struct nlm_file *file))
{
struct hlist_node *next;
struct nlm_file *file;
struct nlm_file *file, *next;
int i, ret = 0;
mutex_lock(&nlm_file_mutex);
for (i = 0; i < FILE_NRHASH; i++) {
hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
if (is_failover_file && !is_failover_file(data, file))
continue;
file = hlist_entry_safe(nlm_files[i].first,
struct nlm_file, f_list);
if (file)
file->f_count++;
mutex_unlock(&nlm_file_mutex);
while (file) {
/*
* Pin the next neighbour before we drop the mutex
* for nlm_inspect_file(); a concurrent
* nlm_release_file() under the same mutex would
* otherwise be free to unlink and kfree it during
* the unlock window, leaving us to dereference a
* freed slab when we walked to next afterwards.
*/
next = hlist_entry_safe(file->f_list.next,
struct nlm_file, f_list);
if (next)
next->f_count++;
/* Traverse locks, blocks and shares of this file
* and update file->f_locks count */
if (nlm_inspect_file(data, file, match))
ret = 1;
if (!is_failover_file || is_failover_file(data, file)) {
mutex_unlock(&nlm_file_mutex);
mutex_lock(&nlm_file_mutex);
file->f_count--;
/* No more references to this file. Let go of it. */
if (list_empty(&file->f_blocks) && !file->f_locks
&& !file->f_shares && !file->f_count) {
hlist_del(&file->f_list);
nlm_close_files(file);
kfree(file);
if (nlm_inspect_file(data, file, match))
ret = 1;
mutex_lock(&nlm_file_mutex);
}
file->f_count--;
nlm_file_release(file);
file = next;
}
}
mutex_unlock(&nlm_file_mutex);
@@ -512,7 +557,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
static int
nlmsvc_match_ip(void *datap, struct nlm_host *host)
{
return rpc_cmp_addr(nlm_srcaddr(host), datap);
return rpc_cmp_addr(nlm_srcaddr(datap), (struct sockaddr *)host);
}
/**

View File

@@ -231,8 +231,9 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
cb_info->users++;
err_net:
if (!cb_info->users) {
xprt_svc_shutdown_bc(xprt);
svc_set_num_threads(cb_info->serv, 0, 0);
svc_destroy(&cb_info->serv);
xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv);
}
err_create:
mutex_unlock(&nfs_callback_mutex);
@@ -254,6 +255,7 @@ void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt)
mutex_lock(&nfs_callback_mutex);
serv = cb_info->serv;
xprt_svc_shutdown_bc(xprt);
nfs_callback_down_net(minorversion, serv, net);
cb_info->users--;
if (cb_info->users == 0) {

View File

@@ -1090,26 +1090,20 @@ static const struct svc_procedure nfs4_callback_procedures1[] = {
}
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nfs4_callback_count1[ARRAY_SIZE(nfs4_callback_procedures1)]);
const struct svc_version nfs4_callback_version1 = {
.vs_vers = 1,
.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
.vs_proc = nfs4_callback_procedures1,
.vs_count = nfs4_callback_count1,
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
.vs_dispatch = nfs_callback_dispatch,
.vs_hidden = true,
.vs_need_cong_ctrl = true,
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nfs4_callback_count4[ARRAY_SIZE(nfs4_callback_procedures1)]);
const struct svc_version nfs4_callback_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
.vs_proc = nfs4_callback_procedures1,
.vs_count = nfs4_callback_count4,
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
.vs_dispatch = nfs_callback_dispatch,
.vs_hidden = true,

View File

@@ -292,8 +292,22 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, cred,
nfs_fh, pnf, fmode);
if (!IS_ERR(localio) && nfs_uuid_add_file(uuid, nfl) < 0) {
/* Delete the cached file when racing with nfs_uuid_put() */
/*
* Delete the cached file when racing with nfs_uuid_put().
* Since nfl->nfs_uuid was never published via
* rcu_assign_pointer(), nfs_close_local_fh() will early-return
* and cannot clean up after us. Drop the slot's file ref and
* its paired net ref, then drop the caller-owned nfsd_file ref
* (+1) and the entry-time nfsd_net ref carried via nf->nf_net,
* and return -ENXIO so the caller never dereferences the
* now-cleared localio.
*/
struct nfsd_file __rcu *tmp =
(struct nfsd_file __force __rcu *)localio;
nfs_to_nfsd_file_put_local(pnf);
nfs_to_nfsd_file_put_local(&tmp);
localio = ERR_PTR(-ENXIO);
}
nfs_to_nfsd_net_put(net);

162
fs/nfsd/attr4.h Normal file
View File

@@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* NFSv4 file attributes supported by this implementation
*/
#ifndef _LINUX_NFSD_ATTR4_H
#define _LINUX_NFSD_ATTR4_H
#include <linux/types.h>
#include <linux/nfs4.h>
/*
* The following attributes are not implemented by NFSD:
* ARCHIVE (deprecated anyway)
* HIDDEN (unlikely to be supported any time soon)
* MIMETYPE (unlikely to be supported any time soon)
* QUOTA_* (unlikely to be supported any time soon)
* SYSTEM (unlikely to be supported any time soon)
* TIME_BACKUP (unlikely to be supported any time soon)
*/
#define NFSD4_SUPPORTED_ATTRS_WORD0 \
(FATTR4_WORD0_SUPPORTED_ATTRS | FATTR4_WORD0_TYPE | FATTR4_WORD0_FH_EXPIRE_TYPE \
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE | FATTR4_WORD0_LINK_SUPPORT \
| FATTR4_WORD0_SYMLINK_SUPPORT | FATTR4_WORD0_NAMED_ATTR | FATTR4_WORD0_FSID \
| FATTR4_WORD0_UNIQUE_HANDLES | FATTR4_WORD0_LEASE_TIME | FATTR4_WORD0_RDATTR_ERROR \
| FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME | FATTR4_WORD0_CASE_INSENSITIVE \
| FATTR4_WORD0_CASE_PRESERVING | FATTR4_WORD0_CHOWN_RESTRICTED \
| FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FILEID | FATTR4_WORD0_FILES_AVAIL \
| FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_HOMOGENEOUS \
| FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \
| FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL)
#define NFSD4_SUPPORTED_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_NO_TRUNC | FATTR4_WORD1_NUMLINKS \
| FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \
| FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \
| FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \
| FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_CREATE \
| FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
#define NFSD4_SUPPORTED_ATTRS_WORD2 0
/* 4.1 */
#ifdef CONFIG_NFSD_PNFS
#define PNFSD_SUPPORTED_ATTRS_WORD1 FATTR4_WORD1_FS_LAYOUT_TYPES
#define PNFSD_SUPPORTED_ATTRS_WORD2 \
(FATTR4_WORD2_LAYOUT_BLKSIZE | FATTR4_WORD2_LAYOUT_TYPES)
#else
#define PNFSD_SUPPORTED_ATTRS_WORD1 0
#define PNFSD_SUPPORTED_ATTRS_WORD2 0
#endif /* CONFIG_NFSD_PNFS */
#define NFSD4_1_SUPPORTED_ATTRS_WORD0 \
NFSD4_SUPPORTED_ATTRS_WORD0
#define NFSD4_1_SUPPORTED_ATTRS_WORD1 \
(NFSD4_SUPPORTED_ATTRS_WORD1 | PNFSD_SUPPORTED_ATTRS_WORD1)
#define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
(NFSD4_SUPPORTED_ATTRS_WORD2 | PNFSD_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_SUPPATTR_EXCLCREAT)
/* 4.2 */
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define NFSD4_2_SECURITY_ATTRS FATTR4_WORD2_SECURITY_LABEL
#else
#define NFSD4_2_SECURITY_ATTRS 0
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
#define NFSD4_2_POSIX_ACL_ATTRS \
(FATTR4_WORD2_ACL_TRUEFORM | \
FATTR4_WORD2_ACL_TRUEFORM_SCOPE | \
FATTR4_WORD2_POSIX_DEFAULT_ACL | \
FATTR4_WORD2_POSIX_ACCESS_ACL)
#else
#define NFSD4_2_POSIX_ACL_ATTRS 0
#endif
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_MODE_UMASK | \
FATTR4_WORD2_CLONE_BLKSIZE | \
NFSD4_2_SECURITY_ATTRS | \
FATTR4_WORD2_XATTR_SUPPORT | \
FATTR4_WORD2_TIME_DELEG_ACCESS | \
FATTR4_WORD2_TIME_DELEG_MODIFY | \
FATTR4_WORD2_OPEN_ARGUMENTS | \
NFSD4_2_POSIX_ACL_ATTRS)
/* These will return ERR_INVAL if specified in GETATTR or READDIR. */
#define NFSD_WRITEONLY_ATTRS_WORD1 \
(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
/*
* These are the only attrs allowed in CREATE/OPEN/SETATTR. Don't add
* a writeable attribute here without also adding code to parse it to
* nfsd4_decode_fattr4().
*/
#define NFSD_WRITEABLE_ATTRS_WORD0 \
(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
#define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_CREATE \
| FATTR4_WORD1_TIME_MODIFY_SET)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL \
FATTR4_WORD2_SECURITY_LABEL
#else
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL 0
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \
FATTR4_WORD2_POSIX_DEFAULT_ACL | FATTR4_WORD2_POSIX_ACCESS_ACL
#else
#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS 0
#endif
#define NFSD_WRITEABLE_ATTRS_WORD2 \
(FATTR4_WORD2_MODE_UMASK \
| MAYBE_FATTR4_WORD2_SECURITY_LABEL \
| FATTR4_WORD2_TIME_DELEG_ACCESS \
| FATTR4_WORD2_TIME_DELEG_MODIFY \
| MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \
)
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
NFSD_WRITEABLE_ATTRS_WORD0
/*
* we currently store the exclusive create verifier in the v_{a,m}time
* attributes so the client can't set these at create time using EXCLUSIVE4_1
*/
#define NFSD_SUPPATTR_EXCLCREAT_WORD1 \
(NFSD_WRITEABLE_ATTRS_WORD1 & \
~(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET))
/*
* The FATTR4_WORD2_TIME_DELEG attributes are not to be allowed for
* OPEN(create) with EXCLUSIVE4_1. It doesn't make sense to set a
* delegated timestamp on a new file.
*
* This mask includes NFSv4.2-only attributes (e.g., POSIX ACLs).
* Version filtering occurs via nfsd_suppattrs[] before this mask
* is applied, so pre-4.2 clients never see unsupported attributes.
*/
#define NFSD_SUPPATTR_EXCLCREAT_WORD2 \
(NFSD_WRITEABLE_ATTRS_WORD2 & \
~(FATTR4_WORD2_TIME_DELEG_ACCESS | FATTR4_WORD2_TIME_DELEG_MODIFY))
extern const u32 nfsd_suppattrs[3][3];
static inline bool bmval_is_subset(const u32 *bm1, const u32 *bm2)
{
return !((bm1[0] & ~bm2[0]) ||
(bm1[1] & ~bm2[1]) ||
(bm1[2] & ~bm2[2]));
}
static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
{
return bmval_is_subset(bmval, nfsd_suppattrs[minorversion]);
}
#endif /* _LINUX_NFSD_ATTR4_H */

View File

@@ -3,6 +3,7 @@
#include <linux/sched.h>
#include "nfsd.h"
#include "export.h"
#include "auth.h"
int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp)
@@ -87,3 +88,21 @@ int nfsd_setuser(struct svc_cred *cred, struct svc_export *exp)
return -ENOMEM;
}
/**
* nfsd_user_namespace - Get user_namespace in effect for an RPC request
* @rqstp: RPC execution context
*
* xpt_cred is set once at transport creation and never modified. The
* transport itself is reference-counted during request processing, so
* no explicit reference on the namespace is necessary.
*
* Return: the user_namespace from the transport credential, or
* init_user_ns if no credential was set. The returned namespace pointer
* is valid for the duration of the RPC request.
*/
struct user_namespace *nfsd_user_namespace(const struct svc_rqst *rqstp)
{
const struct cred *cred = rqstp->rq_xprt->xpt_cred;
return cred ? cred->user_ns : &init_user_ns;
}

View File

@@ -8,10 +8,16 @@
#ifndef LINUX_NFSD_AUTH_H
#define LINUX_NFSD_AUTH_H
struct user_namespace;
struct svc_export;
struct svc_rqst;
/*
* Set the current process's fsuid/fsgid etc to those of the NFS
* client user
*/
int nfsd_setuser(struct svc_cred *cred, struct svc_export *exp);
struct user_namespace *nfsd_user_namespace(const struct svc_rqst *rqstp);
#endif /* LINUX_NFSD_AUTH_H */

View File

@@ -10,7 +10,8 @@
#define NFSCACHE_H
#include <linux/sunrpc/svc.h>
#include "nfsd.h"
struct nfsd_net;
/*
* Representation of a reply cache entry.

View File

@@ -55,6 +55,17 @@
/* We only care about NFSD_MAY_READ/WRITE for this cache */
#define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
/* If the shrinker runs between calls to list_lru_walk_node() in
* nfsd_file_gc(), the "remaining" count will be wrong. This could
* result in premature freeing of some files. This may not matter much
* but is easy to fix with this spinlock which temporarily disables
* the shrinker.
*
* It also serializes callers of nfsd_file_dispose_list_delayed()
* against per-net shutdown.
*/
static DEFINE_SPINLOCK(nfsd_gc_lock);
static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
static DEFINE_PER_CPU(unsigned long, nfsd_file_allocations);
@@ -62,16 +73,12 @@ static DEFINE_PER_CPU(unsigned long, nfsd_file_releases);
static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age);
static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions);
struct nfsd_fcache_disposal {
spinlock_t lock;
struct list_head freeme;
};
static struct kmem_cache *nfsd_file_slab;
static struct kmem_cache *nfsd_file_mark_slab;
static struct list_lru nfsd_file_lru;
static unsigned long nfsd_file_flags;
static struct fsnotify_group *nfsd_file_fsnotify_group;
static struct fsnotify_group *nfsd_dir_fsnotify_group;
static struct delayed_work nfsd_filecache_laundrette;
static struct rhltable nfsd_file_rhltable
____cacheline_aligned_in_smp;
@@ -147,7 +154,7 @@ static void
nfsd_file_mark_put(struct nfsd_file_mark *nfm)
{
if (refcount_dec_and_test(&nfm->nfm_ref)) {
fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group);
fsnotify_destroy_mark(&nfm->nfm_mark, nfm->nfm_mark.group);
fsnotify_put_mark(&nfm->nfm_mark);
}
}
@@ -155,37 +162,40 @@ nfsd_file_mark_put(struct nfsd_file_mark *nfm)
static struct nfsd_file_mark *
nfsd_file_mark_find_or_create(struct inode *inode)
{
int err;
struct fsnotify_mark *mark;
struct nfsd_file_mark *nfm = NULL, *new;
struct fsnotify_group *group;
struct fsnotify_mark *mark;
int err;
group = S_ISDIR(inode->i_mode) ? nfsd_dir_fsnotify_group : nfsd_file_fsnotify_group;
do {
fsnotify_group_lock(nfsd_file_fsnotify_group);
mark = fsnotify_find_inode_mark(inode,
nfsd_file_fsnotify_group);
fsnotify_group_lock(group);
mark = fsnotify_find_inode_mark(inode, group);
if (mark) {
nfm = nfsd_file_mark_get(container_of(mark,
struct nfsd_file_mark,
nfm_mark));
fsnotify_group_unlock(nfsd_file_fsnotify_group);
fsnotify_group_unlock(group);
if (nfm) {
fsnotify_put_mark(mark);
break;
}
/* Avoid soft lockup race with nfsd_file_mark_put() */
fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group);
fsnotify_destroy_mark(mark, group);
fsnotify_put_mark(mark);
} else {
fsnotify_group_unlock(nfsd_file_fsnotify_group);
fsnotify_group_unlock(group);
}
/* allocate a new nfm */
new = kmem_cache_alloc(nfsd_file_mark_slab, GFP_KERNEL);
if (!new)
return NULL;
fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group);
fsnotify_init_mark(&new->nfm_mark, group);
new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF;
refcount_set(&new->nfm_ref, 1);
mutex_init(&new->nfm_recalc_mutex);
err = fsnotify_add_inode_mark(&new->nfm_mark, inode, 0);
@@ -327,8 +337,11 @@ static void nfsd_file_lru_add(struct nfsd_file *nf)
refcount_inc(&nf->nf_ref);
if (list_lru_add_obj(&nfsd_file_lru, &nf->nf_lru))
trace_nfsd_file_lru_add(nf);
else
WARN_ON(1);
else {
refcount_dec(&nf->nf_ref);
WARN_ON_ONCE(1);
return;
}
nfsd_file_schedule_laundrette();
}
@@ -419,25 +432,31 @@ nfsd_file_dispose_list(struct list_head *dispose)
}
/**
* nfsd_file_dispose_list_delayed - move list of dead files to net's freeme list
* nfsd_file_dispose_list_delayed - queue dead files for nfsd thread disposal
* @dispose: list of nfsd_files to be disposed
*
* Transfers each file to the "freeme" list for its nfsd_net, to eventually
* be disposed of by the per-net garbage collector.
* Transfers each file to the dispose list in its nfsd_net and wakes an nfsd
* thread to do the actual close. This keeps the cost of fput() in the nfsd
* threads rather than in the shrinker or GC worker.
*
* All callers must hold nfsd_gc_lock, so that nfsd_file_cache_shutdown_net()
* can synchronize against them before draining the per-net dispose list.
* This guarantees nf_net is still live when we call net_generic().
*/
static void
nfsd_file_dispose_list_delayed(struct list_head *dispose)
{
while(!list_empty(dispose)) {
lockdep_assert_held(&nfsd_gc_lock);
while (!list_empty(dispose)) {
struct nfsd_file *nf = list_first_entry(dispose,
struct nfsd_file, nf_gc);
struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id);
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
struct svc_serv *serv;
spin_lock(&l->lock);
list_move_tail(&nf->nf_gc, &l->freeme);
spin_unlock(&l->lock);
spin_lock(&nn->fcache_dispose_lock);
list_move_tail(&nf->nf_gc, &nn->fcache_dispose_list);
spin_unlock(&nn->fcache_dispose_lock);
/*
* The filecache laundrette is shut down after the
@@ -461,21 +480,28 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose)
*/
void nfsd_file_net_dispose(struct nfsd_net *nn)
{
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
if (!list_empty(&l->freeme)) {
if (!list_empty(&nn->fcache_dispose_list)) {
LIST_HEAD(dispose);
int i;
spin_lock(&l->lock);
for (i = 0; i < 8 && !list_empty(&l->freeme); i++)
list_move(l->freeme.next, &dispose);
spin_unlock(&l->lock);
if (!list_empty(&l->freeme))
/* Wake up another thread to share the work
spin_lock(&nn->fcache_dispose_lock);
for (i = 0; i < 8 && !list_empty(&nn->fcache_dispose_list); i++)
list_move(nn->fcache_dispose_list.next, &dispose);
spin_unlock(&nn->fcache_dispose_lock);
if (!list_empty(&nn->fcache_dispose_list)) {
/*
* Wake up another thread to share the work
* *before* doing any actual disposing.
*
* The filecache laundrette is shut down after
* the nn->nfsd_serv pointer is cleared, but
* before the svc_serv is freed.
*/
svc_wake_up(nn->nfsd_serv);
struct svc_serv *serv = nn->nfsd_serv;
if (serv)
svc_wake_up(serv);
}
nfsd_file_dispose_list(&dispose);
}
}
@@ -552,13 +578,6 @@ nfsd_file_gc_cb(struct list_head *item, struct list_lru_one *lru,
return nfsd_file_lru_cb(item, lru, arg);
}
/* If the shrinker runs between calls to list_lru_walk_node() in
* nfsd_file_gc(), the "remaining" count will be wrong. This could
* result in premature freeing of some files. This may not matter much
* but is easy to fix with this spinlock which temporarily disables
* the shrinker.
*/
static DEFINE_SPINLOCK(nfsd_gc_lock);
static void
nfsd_file_gc(void)
{
@@ -581,9 +600,9 @@ nfsd_file_gc(void)
remaining = 0;
}
}
nfsd_file_dispose_list_delayed(&dispose);
spin_unlock(&nfsd_gc_lock);
trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru));
nfsd_file_dispose_list_delayed(&dispose);
}
static void
@@ -611,9 +630,9 @@ nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
ret = list_lru_shrink_walk(&nfsd_file_lru, sc,
nfsd_file_lru_cb, &dispose);
nfsd_file_dispose_list_delayed(&dispose);
spin_unlock(&nfsd_gc_lock);
trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru));
nfsd_file_dispose_list_delayed(&dispose);
return ret;
}
@@ -686,11 +705,11 @@ nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
}
/**
* nfsd_file_close_inode - attempt a delayed close of a nfsd_file
* nfsd_file_close_inode - attempt a deferred close of a nfsd_file
* @inode: inode of the file to attempt to remove
*
* Close out any open nfsd_files that can be reaped for @inode. The
* actual freeing is deferred to the dispose_list_delayed infrastructure.
* actual freeing is deferred to the nfsd service threads.
*
* This is used by the fsnotify callbacks and setlease notifier.
*/
@@ -699,8 +718,10 @@ nfsd_file_close_inode(struct inode *inode)
{
LIST_HEAD(dispose);
spin_lock(&nfsd_gc_lock);
nfsd_file_queue_for_close(inode, &dispose);
nfsd_file_dispose_list_delayed(&dispose);
spin_unlock(&nfsd_gc_lock);
}
/**
@@ -812,12 +833,36 @@ nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
return 0;
}
#ifdef CONFIG_NFSD_V4
static int
nfsd_dir_fsnotify_handle_event(struct fsnotify_group *group, u32 mask,
const void *data, int data_type, struct inode *dir,
const struct qstr *name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
return nfsd_handle_dir_event(mask, dir, data, data_type, name);
}
#else
static int
nfsd_dir_fsnotify_handle_event(struct fsnotify_group *group, u32 mask,
const void *data, int data_type, struct inode *dir,
const struct qstr *name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
return 0;
}
#endif
static const struct fsnotify_ops nfsd_file_fsnotify_ops = {
.handle_inode_event = nfsd_file_fsnotify_handle_event,
.free_mark = nfsd_file_mark_free,
};
static const struct fsnotify_ops nfsd_dir_fsnotify_ops = {
.handle_event = nfsd_dir_fsnotify_handle_event,
.free_mark = nfsd_file_mark_free,
};
int
nfsd_file_cache_init(void)
{
@@ -869,8 +914,7 @@ nfsd_file_cache_init(void)
goto out_shrinker;
}
nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops,
0);
nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops, 0);
if (IS_ERR(nfsd_file_fsnotify_group)) {
pr_err("nfsd: unable to create fsnotify group: %ld\n",
PTR_ERR(nfsd_file_fsnotify_group));
@@ -879,11 +923,23 @@ nfsd_file_cache_init(void)
goto out_notifier;
}
nfsd_dir_fsnotify_group = fsnotify_alloc_group(&nfsd_dir_fsnotify_ops, 0);
if (IS_ERR(nfsd_dir_fsnotify_group)) {
pr_err("nfsd: unable to create fsnotify group: %ld\n",
PTR_ERR(nfsd_dir_fsnotify_group));
ret = PTR_ERR(nfsd_dir_fsnotify_group);
nfsd_dir_fsnotify_group = NULL;
goto out_notify_group;
}
INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
out:
if (ret)
clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
return ret;
out_notify_group:
fsnotify_put_group(nfsd_file_fsnotify_group);
nfsd_file_fsnotify_group = NULL;
out_notifier:
lease_unregister_notifier(&nfsd_file_lease_notifier);
out_shrinker:
@@ -940,42 +996,14 @@ __nfsd_file_cache_purge(struct net *net)
nfsd_file_dispose_list(&dispose);
}
static struct nfsd_fcache_disposal *
nfsd_alloc_fcache_disposal(void)
{
struct nfsd_fcache_disposal *l;
l = kmalloc_obj(*l);
if (!l)
return NULL;
spin_lock_init(&l->lock);
INIT_LIST_HEAD(&l->freeme);
return l;
}
static void
nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l)
{
nfsd_file_dispose_list(&l->freeme);
kfree(l);
}
static void
nfsd_free_fcache_disposal_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
nfsd_free_fcache_disposal(l);
}
int
nfsd_file_cache_start_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nn->fcache_disposal = nfsd_alloc_fcache_disposal();
return nn->fcache_disposal ? 0 : -ENOMEM;
spin_lock_init(&nn->fcache_dispose_lock);
INIT_LIST_HEAD(&nn->fcache_dispose_list);
return 0;
}
/**
@@ -994,8 +1022,18 @@ nfsd_file_cache_purge(struct net *net)
void
nfsd_file_cache_shutdown_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nfsd_file_cache_purge(net);
nfsd_free_fcache_disposal_net(net);
/*
* Ensure any in-progress shrinker, GC, or fsnotify/lease callback
* (all of which hold nfsd_gc_lock while calling
* nfsd_file_dispose_list_delayed()) has fully completed before
* draining the per-net dispose list.
*/
spin_lock(&nfsd_gc_lock);
spin_unlock(&nfsd_gc_lock);
nfsd_file_dispose_list(&nn->fcache_dispose_list);
}
void
@@ -1019,6 +1057,8 @@ nfsd_file_cache_shutdown(void)
rcu_barrier();
fsnotify_put_group(nfsd_file_fsnotify_group);
nfsd_file_fsnotify_group = NULL;
fsnotify_put_group(nfsd_dir_fsnotify_group);
nfsd_dir_fsnotify_group = NULL;
kmem_cache_destroy(nfsd_file_slab);
nfsd_file_slab = NULL;
fsnotify_wait_marks_destroyed();
@@ -1223,11 +1263,9 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
open_file:
trace_nfsd_file_alloc(nf);
if (type == S_IFREG)
nf->nf_mark = nfsd_file_mark_find_or_create(inode);
if (type != S_IFREG || nf->nf_mark) {
if (file) {
nf->nf_mark = nfsd_file_mark_find_or_create(inode);
if (nf->nf_mark) {
if (file && (file->f_mode & FMODE_OPENED)) {
get_file(file);
nf->nf_file = file;
status = nfs_ok;
@@ -1374,12 +1412,12 @@ nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
* @rqstp: the RPC transaction being executed
* @fhp: the NFS filehandle of the file just created
* @may_flags: NFSD_MAY_ settings for the file
* @file: cached, already-open file (may be NULL)
* @file: cached, already-open file (may be NULL or not yet opened)
* @pnf: OUT: new or found "struct nfsd_file" object
*
* Acquire a nfsd_file object that is not GC'ed. If one doesn't already exist,
* and @file is non-NULL, use it to instantiate a new nfsd_file instead of
* opening a new one.
* and @file has FMODE_OPENED set, use it to instantiate a new nfsd_file
* instead of opening a new one.
*
* Return values:
* %nfs_ok - @pnf points to an nfsd_file with its reference
@@ -1474,3 +1512,54 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
seq_printf(m, "mean age (ms): -\n");
return 0;
}
/**
* nfsd_fsnotify_recalc_mask - recalculate the fsnotify mask for a nfsd_file
* @nf: nfsd_file to recalculate the mask on
*
* When a directory nfsd_file has a delegation added or removed, that may
* change the events that nfsd requires from the VFS layer. This function
* recalculates the fsnotify mask based on the leases present.
*/
void nfsd_fsnotify_recalc_mask(struct nfsd_file *nf)
{
struct inode *inode = file_inode(nf->nf_file);
u32 lease_mask, set = 0, clear = 0;
struct fsnotify_mark *mark;
/* This is only needed when adding or removing dir delegs */
if (!S_ISDIR(inode->i_mode) || !nf->nf_mark)
return;
mark = &nf->nf_mark->nfm_mark;
/*
* The mark is shared by every nfsd_file on this inode, so concurrent
* delegation add/remove on the same directory can recalc it in
* parallel. Serialize the read of the lease state and the update of
* the mark so that a recalc working from a stale snapshot of the
* lease list can't clobber a concurrent recalc's update.
*/
mutex_lock(&nf->nf_mark->nfm_recalc_mutex);
/* Set up notifications for any ignored delegation events */
lease_mask = inode_lease_ignore_mask(inode);
if (lease_mask & FL_IGN_DIR_CREATE)
set |= FS_CREATE | FS_MOVED_TO;
else
clear |= FS_CREATE | FS_MOVED_TO;
if (lease_mask & FL_IGN_DIR_DELETE)
set |= FS_DELETE | FS_MOVED_FROM;
else
clear |= FS_DELETE | FS_MOVED_FROM;
if (lease_mask & FL_IGN_DIR_RENAME)
set |= FS_RENAME;
else
clear |= FS_RENAME;
fsnotify_modify_mark_mask(mark, set, clear);
mutex_unlock(&nf->nf_mark->nfm_recalc_mutex);
}

View File

@@ -26,6 +26,8 @@
struct nfsd_file_mark {
struct fsnotify_mark nfm_mark;
refcount_t nfm_ref;
/* serializes nfsd_fsnotify_recalc_mask() against itself */
struct mutex nfm_recalc_mutex;
};
/*
@@ -86,4 +88,5 @@ __be32 nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
__be32 nfsd_file_acquire_dir(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file **pnf);
int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
void nfsd_fsnotify_recalc_mask(struct nfsd_file *nf);
#endif /* _FS_NFSD_FILECACHE_H */

View File

@@ -30,19 +30,24 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr,
struct ff_idmap uid;
struct ff_idmap gid;
fh_len = 4 + fl->fh.size;
fh_len = 4 + xdr_align_size(fl->fh.size);
uid.len = sprintf(uid.buf, "%u", from_kuid(&init_user_ns, fl->uid));
gid.len = sprintf(gid.buf, "%u", from_kgid(&init_user_ns, fl->gid));
/* 8 + len for recording the length, name, and padding */
ds_len = 20 + sizeof(stateid_opaque_t) + 4 + fh_len +
8 + uid.len + 8 + gid.len;
/* data server entry: deviceid + efficiency + stateid + fh list +
* user + group + flags + stats_collect_hint
*/
ds_len = 16 + 4 + 4 + sizeof(stateid_opaque_t) + 4 + fh_len +
4 + xdr_align_size(uid.len) +
4 + xdr_align_size(gid.len) +
4 + 4;
/* mirror: ds_count + ds */
mirror_len = 4 + ds_len;
/* The layout segment */
len = 20 + mirror_len;
/* stripe_unit + mirror_count + mirror */
len = 12 + mirror_len;
p = xdr_reserve_space(xdr, sizeof(__be32) + len);
if (!p)
@@ -94,7 +99,8 @@ nfsd4_ff_encode_getdeviceinfo(struct xdr_stream *xdr,
}
/* len + padding for two strings */
addr_len = 16 + da->netaddr.netid_len + da->netaddr.addr_len;
addr_len = 8 + xdr_align_size(da->netaddr.netid_len) +
xdr_align_size(da->netaddr.addr_len);
ver_len = 20;
len = 4 + ver_len + 4 + addr_len;

View File

@@ -97,11 +97,15 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
}
nfsd_file_get(localio);
again:
rcu_read_lock();
new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio)));
if (new) {
/* Some other thread installed an nfsd_file */
if (nfsd_file_get(new) == NULL)
if (nfsd_file_get(new) == NULL) {
rcu_read_unlock();
goto again;
}
rcu_read_unlock();
/*
* Drop the ref we were going to install (both file and
* net) and the one we were going to return (only file).
@@ -110,6 +114,8 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
nfsd_net_put(net);
nfsd_file_put(localio);
localio = new;
} else {
rcu_read_unlock();
}
} else
nfsd_net_put(net);
@@ -204,14 +210,11 @@ static const struct svc_procedure localio_procedures1[] = {
};
#define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1)
static DEFINE_PER_CPU_ALIGNED(unsigned long,
localio_count[LOCALIO_NR_PROCEDURES]);
const struct svc_version localio_version1 = {
.vs_vers = 1,
.vs_nproc = LOCALIO_NR_PROCEDURES,
.vs_proc = localio_procedures1,
.vs_dispatch = nfsd_dispatch,
.vs_count = localio_count,
.vs_xdrsize = XDR_QUADLEN(UUID_SIZE),
.vs_hidden = true,
};

View File

@@ -92,6 +92,7 @@ nlm_fclose(struct file *filp)
}
static const struct nlmsvc_binding nfsd_nlm_ops = {
.owner = THIS_MODULE,
.fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */
};
@@ -100,11 +101,12 @@ void
nfsd_lockd_init(void)
{
dprintk("nfsd: initializing lockd\n");
nlmsvc_ops = &nfsd_nlm_ops;
rcu_assign_pointer(nlmsvc_ops, &nfsd_nlm_ops);
}
void
nfsd_lockd_shutdown(void)
{
nlmsvc_ops = NULL;
RCU_INIT_POINTER(nlmsvc_ops, NULL);
synchronize_rcu();
}

View File

@@ -225,6 +225,11 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
.maxattr = NFSD_A_UNLOCK_EXPORT_PATH,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = NFSD_CMD_SERVER_STATS_GET,
.dumpit = nfsd_nl_server_stats_get_dumpit,
.flags = GENL_CMD_CAP_DUMP,
},
};
static const struct genl_multicast_group nfsd_nl_mcgrps[] = {

View File

@@ -42,6 +42,8 @@ int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_server_stats_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
enum {
NFSD_NLGRP_NONE,

View File

@@ -28,6 +28,16 @@ struct cld_net;
struct nfsd_net_cb;
struct nfsd4_client_tracking_ops;
enum nfsd_net_flag {
NFSD_NET_GRACE_ENDED,
NFSD_NET_GRACE_END_FORCED,
NFSD_NET_IN_GRACE,
NFSD_NET_SOMEBODY_RECLAIMED,
NFSD_NET_TRACK_RECLAIM_COMPLETES,
NFSD_NET_UP,
NFSD_NET_LOCKD_UP,
};
enum {
/* cache misses due only to checksum comparison failures */
NFSD_STATS_PAYLOAD_MISSES,
@@ -43,11 +53,16 @@ enum {
NFSD_STATS_FIRST_NFS4_OP, /* count of individual nfsv4 operations */
NFSD_STATS_LAST_NFS4_OP = NFSD_STATS_FIRST_NFS4_OP + LAST_NFS4_OP,
#define NFSD_STATS_NFS4_OP(op) (NFSD_STATS_FIRST_NFS4_OP + (op))
NFSD_STATS_WDELEG_GETATTR, /* count of getattr conflict with wdeleg */
#endif
NFSD_STATS_COUNTERS_NUM
};
/*
* Per-netns NFSv4 callback (backchannel) per-operation counters, indexed
* directly by RFC 8881 callback opcode (OP_CB_GETATTR..OP_CB_OFFLOAD).
*/
#define NFSD_STATS_CB_OPS_NUM (OP_CB_OFFLOAD + 1)
/*
* Represents a nfsd "container". With respect to nfsv4 state tracking, the
* fields of interest are the *_id_hashtbls and the *_name_tree. These track
@@ -66,9 +81,9 @@ struct nfsd_net {
struct cache_detail *nametoid_cache;
struct lock_manager nfsd4_manager;
bool grace_ended;
bool grace_end_forced;
unsigned long flags;
time64_t boot_time;
time64_t boot_time_bt; /* same instant in CLOCK_BOOTTIME */
struct dentry *nfsd_client_dir;
@@ -84,6 +99,7 @@ struct nfsd_net {
*/
struct list_head *reclaim_str_hashtbl;
int reclaim_str_hashtbl_size;
struct rw_semaphore reclaim_str_hashtbl_lock;
struct list_head *conf_id_hashtbl;
struct rb_root conf_name_tree;
struct list_head *unconf_id_hashtbl;
@@ -96,12 +112,16 @@ struct nfsd_net {
* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
* for last close replay.
*
* All of the above fields are protected by the client_mutex.
* reclaim_str_hashtbl[], reclaim_str_hashtbl_size are protected by
* reclaim_str_hashtbl_lock.
*
* All of the remaining fields are protected by the client_lock.
*/
struct list_head client_lru;
struct list_head close_lru;
/* protects del_recall_lru and delegation hash/unhash */
/* protects del_recall_lru and delegation hash/unhash;
* nests outside client_lock */
spinlock_t deleg_lock ____cacheline_aligned;
struct list_head del_recall_lru;
@@ -110,26 +130,21 @@ struct nfsd_net {
struct delayed_work laundromat_work;
/* client_lock protects the client lru list and session hash table */
/* client_lock protects the client lru list and session hash
* table; nests inside deleg_lock */
spinlock_t client_lock;
/* protects blocked_locks_lru */
spinlock_t blocked_locks_lock;
struct file *rec_file;
bool in_grace;
const struct nfsd4_client_tracking_ops *client_tracking_ops;
time64_t nfsd4_lease;
time64_t nfsd4_grace;
bool somebody_reclaimed;
bool track_reclaim_completes;
atomic_t nr_reclaim_complete;
bool nfsd_net_up;
bool lockd_up;
seqlock_t writeverf_lock;
unsigned char writeverf[8];
@@ -190,6 +205,11 @@ struct nfsd_net {
/* Per-netns stats counters */
struct percpu_counter counter[NFSD_STATS_COUNTERS_NUM];
#ifdef CONFIG_NFSD_V4
/* Per-netns NFSv4 callback (backchannel) per-operation counters */
struct percpu_counter cb_counter[NFSD_STATS_CB_OPS_NUM];
#endif
/* sunrpc svc stats */
struct svc_stat nfsd_svcstats;
@@ -209,7 +229,8 @@ struct nfsd_net {
/* utsname taken from the process that starts the server */
char nfsd_name[UNX_MAXNODENAME+1];
struct nfsd_fcache_disposal *fcache_disposal;
spinlock_t fcache_dispose_lock;
struct list_head fcache_dispose_list;
siphash_key_t siphash_key;

View File

@@ -115,14 +115,19 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
inode_lock(inode);
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS,
argp->acl_access);
if (error)
goto out_drop_lock;
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT,
argp->acl_default);
if (error)
goto out_drop_lock;
error = 0;
if (argp->mask & NFS_ACL) {
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
ACL_TYPE_ACCESS, argp->acl_access);
if (error)
goto out_drop_lock;
}
if (argp->mask & NFS_DFACL) {
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
ACL_TYPE_DEFAULT, argp->acl_default);
if (error)
goto out_drop_lock;
}
inode_unlock(inode);
@@ -248,22 +253,21 @@ nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
if (!svcxdr_encode_stat(xdr, resp->status))
return false;
if (dentry == NULL || d_really_is_negative(dentry))
return true;
inode = d_inode(dentry);
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
return false;
if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
return false;
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
resp->mask & NFS_ACL, 0))
return false;
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
resp->mask & NFS_DFACL, NFS_ACL_DEFAULT))
return false;
switch (resp->status) {
case nfs_ok:
inode = d_inode(dentry);
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
return false;
if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
return false;
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
resp->mask & NFS_ACL, 0))
return false;
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
resp->mask & NFS_DFACL, NFS_ACL_DEFAULT))
return false;
break;
}
return true;
}
@@ -384,13 +388,10 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
},
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)]);
const struct svc_version nfsd_acl_version2 = {
.vs_vers = 2,
.vs_nproc = ARRAY_SIZE(nfsd_acl_procedures2),
.vs_proc = nfsd_acl_procedures2,
.vs_count = nfsd_acl_count2,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
};

View File

@@ -105,12 +105,17 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
inode_lock(inode);
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS,
argp->acl_access);
if (error)
goto out_drop_lock;
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT,
argp->acl_default);
error = 0;
if (argp->mask & NFS_ACL) {
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
ACL_TYPE_ACCESS, argp->acl_access);
if (error)
goto out_drop_lock;
}
if (argp->mask & NFS_DFACL) {
error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
ACL_TYPE_DEFAULT, argp->acl_default);
}
out_drop_lock:
inode_unlock(inode);
@@ -273,13 +278,10 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
},
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)]);
const struct svc_version nfsd_acl_version3 = {
.vs_vers = 3,
.vs_nproc = ARRAY_SIZE(nfsd_acl_procedures3),
.vs_proc = nfsd_acl_procedures3,
.vs_count = nfsd_acl_count3,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
};

View File

@@ -29,6 +29,25 @@ static int nfs3_ftypes[] = {
S_IFIFO, /* NF3FIFO */
};
/*
* Reject a client-supplied atime or mtime whose nanoseconds field is out
* of range. Such a value is well-formed on the wire but is not a valid
* timespec64, and storing it verbatim can corrupt on-disk timestamps.
* tv_nsec is a long, so it is cast to unsigned long (the same width) to
* catch both an over-large value and one that became negative when an
* out-of-range u32 wire nseconds was assigned to a 32-bit long.
*/
static bool nfsd3_time_in_range(const struct iattr *iap)
{
if ((iap->ia_valid & ATTR_ATIME_SET) &&
(unsigned long)iap->ia_atime.tv_nsec >= NSEC_PER_SEC)
return false;
if ((iap->ia_valid & ATTR_MTIME_SET) &&
(unsigned long)iap->ia_mtime.tv_nsec >= NSEC_PER_SEC)
return false;
return true;
}
static __be32 nfsd3_map_status(__be32 status)
{
switch (status) {
@@ -101,9 +120,14 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp)
SVCFH_fmt(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
if (!nfsd3_time_in_range(&argp->attrs)) {
resp->status = nfserr_inval;
goto out;
}
if (argp->check_guard)
guardtime = &argp->guardtime;
resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, guardtime);
out:
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
}
@@ -265,7 +289,9 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
trace_nfsd_vfs_create(rqstp, fhp, S_IFREG, argp->name, argp->len);
if (isdotent(argp->name, argp->len))
if (!nfsd3_time_in_range(iap))
return nfserr_inval;
if (name_is_dot_dotdot(argp->name, argp->len))
return nfserr_exist;
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
@@ -400,8 +426,13 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp)
argp->attrs.ia_valid &= ~ATTR_SIZE;
fh_copy(&resp->dirfh, &argp->fh);
fh_init(&resp->fh, NFS3_FHSIZE);
if (!nfsd3_time_in_range(&argp->attrs)) {
resp->status = nfserr_inval;
goto out;
}
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&attrs, S_IFDIR, 0, &resp->fh);
out:
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
}
@@ -415,6 +446,10 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
.na_iattr = &argp->attrs,
};
if (!nfsd3_time_in_range(&argp->attrs)) {
resp->status = nfserr_inval;
goto out;
}
if (argp->tlen == 0) {
resp->status = nfserr_inval;
goto out;
@@ -471,6 +506,11 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp)
goto out;
}
if (!nfsd3_time_in_range(&argp->attrs)) {
resp->status = nfserr_inval;
goto out;
}
type = nfs3_ftypes[argp->ftype];
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&attrs, type, rdev, &resp->fh);
@@ -1068,13 +1108,10 @@ static const struct svc_procedure nfsd_procedures3[22] = {
},
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nfsd_count3[ARRAY_SIZE(nfsd_procedures3)]);
const struct svc_version nfsd_version3 = {
.vs_vers = 3,
.vs_nproc = ARRAY_SIZE(nfsd_procedures3),
.vs_proc = nfsd_procedures3,
.vs_dispatch = nfsd_dispatch,
.vs_count = nfsd_count3,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
};

View File

@@ -987,7 +987,7 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
dparent = cd->fh.fh_dentry;
exp = cd->fh.fh_export;
if (isdotent(name, namlen)) {
if (name_is_dot_dotdot(name, namlen)) {
if (namlen == 2) {
dchild = dget_parent(dparent);
/*

View File

@@ -39,6 +39,7 @@
#include "nfsd.h"
#include "state.h"
#include "netns.h"
#include "stats.h"
#include "trace.h"
#include "xdr4cb.h"
#include "xdr4.h"
@@ -108,6 +109,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access))
return -EIO;
if (access.nseconds >= NSEC_PER_SEC)
return -EIO;
fattr->ncf_cb_atime.tv_sec = access.seconds;
fattr->ncf_cb_atime.tv_nsec = access.nseconds;
@@ -117,6 +120,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify))
return -EIO;
if (modify.nseconds >= NSEC_PER_SEC)
return -EIO;
fattr->ncf_cb_mtime.tv_sec = modify.seconds;
fattr->ncf_cb_mtime.tv_nsec = modify.nseconds;
@@ -456,13 +461,20 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
const struct nfsd4_callback *cb,
struct nfs4_cb_compound_hdr *hdr)
{
struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
struct nfsd4_session *session;
struct nfsd4_referring_call_list *rcl;
__be32 *p;
if (hdr->minorversion == 0)
return;
rcu_read_lock();
session = rcu_dereference(cb->cb_clp->cl_cb_session);
if (!session) {
rcu_read_unlock();
return;
}
encode_nfs_cb_opnum4(xdr, OP_CB_SEQUENCE);
encode_sessionid4(xdr, session);
@@ -478,6 +490,7 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
encode_referring_call_list4(xdr, rcl);
hdr->nops++;
rcu_read_unlock();
}
static void update_cb_slot_table(struct nfsd4_session *ses, u32 target)
@@ -529,21 +542,32 @@ static void update_cb_slot_table(struct nfsd4_session *ses, u32 target)
static int decode_cb_sequence4resok(struct xdr_stream *xdr,
struct nfsd4_callback *cb)
{
struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
struct nfsd4_session *session;
int status = -ESERVERFAULT;
__be32 *p;
u32 seqid, slotid, target;
rcu_read_lock();
session = rcu_dereference(cb->cb_clp->cl_cb_session);
if (!session) {
rcu_read_unlock();
cb->cb_seq_status = -NFS4ERR_BADSESSION;
return -NFS4ERR_BADSESSION;
}
/*
* If the server returns different values for sessionID, slotID or
* sequence number, the server is looney tunes.
*/
p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4 + 4 + 4);
if (unlikely(p == NULL))
if (unlikely(p == NULL)) {
rcu_read_unlock();
goto out_overflow;
}
if (memcmp(p, session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
dprintk("NFS: %s Invalid session id\n", __func__);
rcu_read_unlock();
goto out;
}
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
@@ -551,12 +575,14 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
seqid = be32_to_cpup(p++);
if (seqid != session->se_cb_seq_nr[cb->cb_held_slot]) {
dprintk("NFS: %s Invalid sequence number\n", __func__);
rcu_read_unlock();
goto out;
}
slotid = be32_to_cpup(p++);
if (slotid != cb->cb_held_slot) {
dprintk("NFS: %s Invalid slotid\n", __func__);
rcu_read_unlock();
goto out;
}
@@ -564,6 +590,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
target = be32_to_cpup(p++);
update_cb_slot_table(session, target);
rcu_read_unlock();
status = 0;
out:
cb->cb_seq_status = status;
@@ -865,6 +892,84 @@ static void encode_stateowner(struct xdr_stream *xdr, struct nfs4_stateowner *so
xdr_encode_opaque(p, so->so_owner.data, so->so_owner.len);
}
static void nfs4_xdr_enc_cb_notify(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfsd4_callback *cb = data;
struct nfsd4_cb_notify *ncn = container_of(cb, struct nfsd4_cb_notify, ncn_cb);
struct nfs4_delegation *dp = container_of(ncn, struct nfs4_delegation, dl_cb_notify);
struct nfs4_cb_compound_hdr hdr = {
.ident = 0,
.minorversion = cb->cb_clp->cl_minorversion,
};
struct CB_NOTIFY4args args;
unsigned int start;
WARN_ON_ONCE(hdr.minorversion == 0);
encode_cb_compound4args(xdr, &hdr);
encode_cb_sequence4args(xdr, cb, &hdr);
/*
* nfsd4_cb_notify_prepare() sized the payload against a single page,
* but did not account for the compound, sequence, stateid, and
* filehandle encoded here. If the variable-length encode overflows the
* backchannel send buffer, roll back to before the operation so that a
* truncated CB_NOTIFY is never placed on the wire.
*/
start = xdr_stream_pos(xdr);
if (xdr_stream_encode_u32(xdr, OP_CB_NOTIFY) < 0)
goto out_err;
args.cna_stateid.seqid = dp->dl_stid.sc_stateid.si_generation;
memcpy(&args.cna_stateid.other, &dp->dl_stid.sc_stateid.si_opaque,
ARRAY_SIZE(args.cna_stateid.other));
args.cna_fh.len = dp->dl_stid.sc_file->fi_fhandle.fh_size;
args.cna_fh.data = dp->dl_stid.sc_file->fi_fhandle.fh_raw;
args.cna_changes.count = ncn->ncn_nf_cnt;
args.cna_changes.element = ncn->ncn_nf;
if (!xdrgen_encode_CB_NOTIFY4args(xdr, &args))
goto out_err;
hdr.nops++;
encode_cb_nops(&hdr);
return;
out_err:
/*
* Drop the CB_NOTIFY op and emit a valid CB_SEQUENCE-only compound so
* the client still advances its slot. Flag the failure so the done
* handler recalls the delegation and the missed notification is not
* silently lost. The flag is written here in the transmit path and read
* in the done handler; the two are serialized phases of the same
* rpc_task, so no additional barrier is needed.
*/
ncn->ncn_encode_err = true;
xdr_truncate_encode(xdr, start);
encode_cb_nops(&hdr);
}
static int nfs4_xdr_dec_cb_notify(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfsd4_callback *cb = data;
struct nfs4_cb_compound_hdr hdr;
int status;
status = decode_cb_compound4res(xdr, &hdr);
if (unlikely(status))
return status;
status = decode_cb_sequence4res(xdr, cb);
if (unlikely(status || cb->cb_seq_status))
return status;
return decode_cb_op_status(xdr, OP_CB_NOTIFY, &cb->cb_status);
}
static void nfs4_xdr_enc_cb_notify_lock(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
@@ -1026,6 +1131,7 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = {
#ifdef CONFIG_NFSD_PNFS
PROC(CB_LAYOUT, COMPOUND, cb_layout, cb_layout),
#endif
PROC(CB_NOTIFY, COMPOUND, cb_notify, cb_notify),
PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock),
PROC(CB_OFFLOAD, COMPOUND, cb_offload, cb_offload),
PROC(CB_RECALL_ANY, COMPOUND, cb_recall_any, cb_recall_any),
@@ -1150,9 +1256,8 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
} else {
if (!conn->cb_xprt || !ses)
return -EINVAL;
clp->cl_cb_session = ses;
args.bc_xprt = conn->cb_xprt;
args.prognumber = clp->cl_cb_session->se_cb_prog;
args.prognumber = ses->se_cb_prog;
args.protocol = conn->cb_xprt->xpt_class->xcl_ident |
XPRT_TRANSPORT_BC;
args.authflavor = ses->se_cb_sec.flavor;
@@ -1170,8 +1275,10 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
return -ENOMEM;
}
if (clp->cl_minorversion != 0)
if (clp->cl_minorversion != 0) {
clp->cl_cb_conn.cb_xprt = conn->cb_xprt;
rcu_assign_pointer(clp->cl_cb_session, ses);
}
clp->cl_cb_client = client;
clp->cl_cb_cred = cred;
rcu_read_lock();
@@ -1278,18 +1385,33 @@ static int grab_slot(struct nfsd4_session *ses)
static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd4_session *ses = clp->cl_cb_session;
struct nfsd4_session *ses;
if (cb->cb_held_slot >= 0)
return true;
rcu_read_lock();
ses = rcu_dereference(clp->cl_cb_session);
if (!ses) {
rcu_read_unlock();
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
return false;
}
cb->cb_held_slot = grab_slot(ses);
if (cb->cb_held_slot < 0) {
rcu_read_unlock();
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
/* Race breaker */
cb->cb_held_slot = grab_slot(ses);
rcu_read_lock();
ses = rcu_dereference(clp->cl_cb_session);
if (ses)
cb->cb_held_slot = grab_slot(ses);
rcu_read_unlock();
if (cb->cb_held_slot < 0)
return false;
rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
} else {
rcu_read_unlock();
}
return true;
}
@@ -1297,12 +1419,17 @@ static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
static void nfsd41_cb_release_slot(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd4_session *ses = clp->cl_cb_session;
struct nfsd4_session *ses;
if (cb->cb_held_slot >= 0) {
spin_lock(&ses->se_lock);
ses->se_cb_slot_avail |= BIT(cb->cb_held_slot);
spin_unlock(&ses->se_lock);
rcu_read_lock();
ses = rcu_dereference(clp->cl_cb_session);
if (ses) {
spin_lock(&ses->se_lock);
ses->se_cb_slot_avail |= BIT(cb->cb_held_slot);
spin_unlock(&ses->se_lock);
}
rcu_read_unlock();
cb->cb_held_slot = -1;
rpc_wake_up_next(&clp->cl_cb_waitq);
}
@@ -1319,6 +1446,16 @@ static void nfsd41_destroy_cb(struct nfsd4_callback *cb)
else
clear_bit(NFSD4_CALLBACK_RUNNING, &cb->cb_flags);
/*
* Order the clear of NFSD4_CALLBACK_RUNNING above before the ->release()
* callback below. A release op may re-check producer-side state to decide
* whether to requeue itself (see nfsd4_cb_notify_release()), and that
* check must not be reordered ahead of the clear. The plain clear_bit()
* path carries no ordering; clear_and_wake_up_bit() already issues this
* barrier internally, so the extra one is harmless there.
*/
smp_mb__after_atomic();
if (cb->cb_ops && cb->cb_ops->release)
cb->cb_ops->release(cb);
nfsd41_cb_inflight_end(clp);
@@ -1434,22 +1571,35 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
trace_nfsd_cb_rpc_prepare(clp);
cb->cb_seq_status = 1;
cb->cb_status = 0;
if (minorversion && !nfsd41_cb_get_slot(cb, task))
return;
if (minorversion) {
if (!rcu_access_pointer(clp->cl_cb_session)) {
rpc_exit(task, -EIO);
return;
}
if (!nfsd41_cb_get_slot(cb, task))
return;
}
rpc_call_start(task);
}
/* Returns true if CB_COMPOUND processing should continue */
static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback *cb)
{
struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
struct nfsd4_session *session;
bool ret = false;
if (cb->cb_held_slot < 0)
goto requeue;
rcu_read_lock();
session = rcu_dereference(cb->cb_clp->cl_cb_session);
if (!session) {
rcu_read_unlock();
goto requeue;
}
/* This is the operation status code for CB_SEQUENCE */
trace_nfsd_cb_seq_status(task, cb);
trace_nfsd_cb_seq_status(task, cb, session);
switch (cb->cb_seq_status) {
case 0:
/*
@@ -1481,12 +1631,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
fallthrough;
case -NFS4ERR_BADSESSION:
nfsd4_mark_cb_fault(cb->cb_clp);
rcu_read_unlock();
goto requeue;
case -NFS4ERR_DELAY:
cb->cb_seq_status = 1;
if (RPC_SIGNALLED(task) || !rpc_restart_call(task))
if (RPC_SIGNALLED(task) || !rpc_restart_call(task)) {
rcu_read_unlock();
goto requeue;
}
rpc_delay(task, 2 * HZ);
rcu_read_unlock();
return false;
case -NFS4ERR_SEQ_MISORDERED:
case -NFS4ERR_BADSLOT:
@@ -1498,11 +1652,13 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
*/
nfsd4_mark_cb_fault(cb->cb_clp);
cb->cb_held_slot = -1;
rcu_read_unlock();
goto retry_nowait;
default:
nfsd4_mark_cb_fault(cb->cb_clp);
}
trace_nfsd_cb_free_slot(task, cb);
trace_nfsd_cb_free_slot(task, cb, session);
rcu_read_unlock();
nfsd41_cb_release_slot(cb);
return ret;
retry_nowait:
@@ -1624,7 +1780,15 @@ static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp)
* Note there isn't a lot of locking in this code; instead we depend on
* the fact that it is run from clp->cl_callback_wq, which won't run two
* work items at once. So, for example, clp->cl_callback_wq handles all
* access of cl_cb_client and all calls to rpc_create or rpc_shutdown_client.
* access of cl_cb_client, and all calls to rpc_create or
* rpc_shutdown_client.
*
* cl_cb_session is written only from cl_callback_wq (via
* rcu_assign_pointer) and read from rpciod under rcu_read_lock (via
* rcu_dereference) by encode_cb_sequence4args(), decode_cb_sequence4resok(),
* nfsd4_cb_sequence_done(), and the cb-slot helpers. Sessions are freed
* with kfree_rcu() so that rpciod readers in an RCU read-side critical
* section never dereference a freed session.
*/
static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
{
@@ -1676,6 +1840,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
nfsd4_mark_cb_down(clp);
if (c)
svc_xprt_put(c->cn_xprt);
rcu_assign_pointer(clp->cl_cb_session, ses);
return;
}
}
@@ -1715,7 +1880,10 @@ nfsd4_run_cb_work(struct work_struct *work)
if (!test_and_clear_bit(NFSD4_CALLBACK_REQUEUE, &cb->cb_flags)) {
if (cb->cb_ops && cb->cb_ops->prepare)
cb->cb_ops->prepare(cb);
if (!cb->cb_ops->prepare(cb)) {
nfsd41_destroy_cb(cb);
return;
}
}
cb->cb_msg.rpc_cred = clp->cl_cb_cred;
@@ -1754,12 +1922,31 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
bool nfsd4_run_cb(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
const struct nfsd4_callback_ops *ops = cb->cb_ops;
u32 minorversion = clp->cl_minorversion;
bool queued;
nfsd41_cb_inflight_begin(clp);
queued = nfsd4_queue_cb(cb);
if (!queued)
if (queued) {
if (ops) {
nfsd_stats_cb_op_inc(nn, ops->opcode);
/*
* Minorversion > 0 callbacks prepend a CB_SEQUENCE op
* (see encode_cb_sequence4args()); count it like the
* forechannel counts SEQUENCE, so it isn't perpetually
* reported as zero. CB_NULL probes (ops == NULL) carry
* no CB_SEQUENCE -- and on 4.1+ they are dropped without
* sending any RPC (see nfsd4_run_cb_work()) -- so they
* must not be counted here.
*/
if (minorversion > 0)
nfsd_stats_cb_op_inc(nn, OP_CB_SEQUENCE);
}
} else {
nfsd41_cb_inflight_end(clp);
}
return queued;
}

View File

@@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/sunrpc/svc_xprt.h>
#include <net/net_namespace.h>
#include "auth.h"
#include "idmap.h"
#include "nfsd.h"
#include "netns.h"

View File

@@ -247,13 +247,21 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
nfsd4_init_cb(&ls->ls_recall, clp, &nfsd4_cb_layout_ops,
NFSPROC4_CLNT_CB_LAYOUT);
if (parent->sc_type == SC_TYPE_DELEG)
ls->ls_file = nfsd_file_get(fp->fi_deleg_file);
else
if (parent->sc_type == SC_TYPE_DELEG) {
rcu_read_lock();
ls->ls_file = nfsd_file_get(rcu_dereference(fp->fi_deleg_file));
rcu_read_unlock();
} else {
ls->ls_file = find_any_file(fp);
BUG_ON(!ls->ls_file);
}
if (!ls->ls_file) {
nfs4_put_stid(stp);
return NULL;
}
ls->ls_fenced = false;
ls->ls_fence_inflight = false;
ls->ls_fence_delay = 0;
INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker);
@@ -652,7 +660,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls, struct nfsd_file *file)
}
}
static void
static bool
nfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
{
struct nfs4_layout_stateid *ls =
@@ -661,6 +669,7 @@ nfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
mutex_lock(&ls->ls_mutex);
nfs4_inc_and_copy_stateid(&ls->ls_recall_sid, &ls->ls_stid);
mutex_unlock(&ls->ls_mutex);
return true;
}
static int
@@ -791,15 +800,6 @@ nfsd4_layout_fence_worker(struct work_struct *work)
struct nfs4_client *clp;
struct nfsd_net *nn;
/*
* The workqueue clears WORK_STRUCT_PENDING before invoking
* this callback. Re-arm immediately so that
* delayed_work_pending() returns true while the fence
* operation is in progress, preventing
* lm_breaker_timedout() from taking a duplicate reference.
*/
mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0);
spin_lock(&ls->ls_lock);
if (list_empty(&ls->ls_layouts)) {
spin_unlock(&ls->ls_lock);
@@ -809,6 +809,9 @@ nfsd4_layout_fence_worker(struct work_struct *work)
nfsd4_close_layout(ls);
ls->ls_fenced = true;
spin_lock(&ls->ls_lock);
ls->ls_fence_inflight = false;
spin_unlock(&ls->ls_lock);
nfs4_put_stid(&ls->ls_stid);
return;
}
@@ -894,18 +897,26 @@ nfsd4_layout_lm_breaker_timedout(struct file_lease *fl)
if ((!nfsd4_layout_ops[ls->ls_layout_type]->fence_client) ||
ls->ls_fenced)
return true;
if (delayed_work_pending(&ls->ls_fence_work))
return false;
/*
* Make sure layout has not been returned yet before
* taking a reference count on the layout stateid.
* taking a reference count on the layout stateid. The
* ls_fence_inflight flag is set together with the sc_count
* increment under ls_lock so that a fence worker invocation
* already in progress (which has cleared WORK_STRUCT_PENDING
* but not yet reached dispose:) cannot be coalesced with a
* fresh schedule that takes an extra unmatched reference.
*/
spin_lock(&ls->ls_lock);
if (ls->ls_fence_inflight) {
spin_unlock(&ls->ls_lock);
return false;
}
if (list_empty(&ls->ls_layouts) ||
!refcount_inc_not_zero(&ls->ls_stid.sc_count)) {
spin_unlock(&ls->ls_lock);
return true;
}
ls->ls_fence_inflight = true;
spin_unlock(&ls->ls_lock);
mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0);

View File

@@ -42,6 +42,7 @@
#include <linux/sunrpc/addr.h>
#include <linux/nfs_ssc.h>
#include "attr4.h"
#include "idmap.h"
#include "cache.h"
#include "xdr4.h"
@@ -57,7 +58,7 @@ module_param(inter_copy_offload_enable, bool, 0644);
MODULE_PARM_DESC(inter_copy_offload_enable,
"Enable inter server to server copy offload. Default: false");
static void cleanup_async_copy(struct nfsd4_copy *copy);
static void cleanup_async_copy(struct nfsd4_async_copy *copy);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
static int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */
@@ -259,7 +260,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
__be32 status;
int host_err;
if (isdotent(open->op_fname, open->op_fnamelen))
if (name_is_dot_dotdot(open->op_fname, open->op_fnamelen))
return nfserr_exist;
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
@@ -306,10 +307,6 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
goto out;
}
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
v_mtime = 0;
v_atime = 0;
if (nfsd4_create_is_exclusive(open->op_createmode)) {
@@ -335,6 +332,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (status != nfs_ok)
goto out;
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
switch (open->op_createmode) {
case NFS4_CREATE_UNCHECKED:
if (!d_is_reg(child))
@@ -385,6 +386,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
open->op_created = true;
fh_fill_post_attrs(fhp);
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
/* A newly created file already has a file size of zero. */
if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
iap->ia_valid &= ~ATTR_SIZE;
@@ -663,7 +668,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
pr_warn("nfsd4_process_open2 failed to open newly-created file: status=%u\n",
be32_to_cpu(status));
if (reclaim && !status)
nn->somebody_reclaimed = true;
set_bit(NFSD_NET_SOMEBODY_RECLAIMED, &nn->flags);
out:
if (open->op_filp) {
fput(open->op_filp);
@@ -677,8 +682,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfsd4_cleanup_open_state(cstate, open);
nfsd4_bump_seqid(cstate, status);
out_err:
posix_acl_release(open->op_dpacl);
posix_acl_release(open->op_pacl);
return status;
}
@@ -700,6 +703,13 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
return nfsd4_open(rqstp, cstate, &op->u);
}
static void
nfsd4_open_release(union nfsd4_op_u *u)
{
posix_acl_release(u->open.op_dpacl);
posix_acl_release(u->open.op_pacl);
}
/*
* filehandle-manipulating ops.
*/
@@ -839,6 +849,20 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
goto out_aftermask;
/* Sanitize cr_type to avoid returning ATTRNOTSUPP. */
switch (create->cr_type) {
case NF4LNK:
case NF4BLK:
case NF4CHR:
case NF4SOCK:
case NF4FIFO:
case NF4DIR:
break;
default:
status = nfserr_badtype;
goto out_aftermask;
}
if (create->cr_acl) {
if (attrs.na_dpacl || attrs.na_pacl) {
status = nfserr_inval;
@@ -846,6 +870,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl,
&attrs);
if (status != nfs_ok)
goto out_aftermask;
}
current->fs->umask = create->cr_umask;
switch (create->cr_type) {
@@ -1253,7 +1279,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (deleg_attrs) {
status = nfserr_bad_stateid;
if (st->sc_type & SC_TYPE_DELEG) {
if (st && (st->sc_type & SC_TYPE_DELEG)) {
struct nfs4_delegation *dp = delegstateid(st);
/* Only for *_ATTRS_DELEG flavors */
@@ -1440,13 +1466,13 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
*/
bool nfsd4_has_active_async_copies(struct nfs4_client *clp)
{
struct nfsd4_copy *copy;
struct nfsd4_async_copy *copy;
bool result = false;
spin_lock(&clp->async_lock);
list_for_each_entry(copy, &clp->async_copies, copies) {
if (!test_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags) &&
!test_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) {
if (!test_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_copy.cp_flags) &&
!test_bit(NFSD4_COPY_F_STOPPED, &copy->cp_copy.cp_flags)) {
result = true;
break;
}
@@ -1462,7 +1488,7 @@ bool nfsd4_has_active_async_copies(struct nfs4_client *clp)
void nfsd4_async_copy_reaper(struct nfsd_net *nn)
{
struct nfs4_client *clp;
struct nfsd4_copy *copy;
struct nfsd4_async_copy *copy;
LIST_HEAD(reaplist);
spin_lock(&nn->client_lock);
@@ -1471,8 +1497,9 @@ void nfsd4_async_copy_reaper(struct nfsd_net *nn)
spin_lock(&clp->async_lock);
list_for_each_safe(pos, next, &clp->async_copies) {
copy = list_entry(pos, struct nfsd4_copy, copies);
if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags)) {
copy = list_entry(pos, struct nfsd4_async_copy, copies);
if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE,
&copy->cp_copy.cp_flags)) {
if (!--copy->cp_ttl) {
list_del_init(&copy->copies);
list_add(&copy->copies, &reaplist);
@@ -1484,53 +1511,56 @@ void nfsd4_async_copy_reaper(struct nfsd_net *nn)
spin_unlock(&nn->client_lock);
while (!list_empty(&reaplist)) {
copy = list_first_entry(&reaplist, struct nfsd4_copy, copies);
copy = list_first_entry(&reaplist, struct nfsd4_async_copy,
copies);
list_del_init(&copy->copies);
cleanup_async_copy(copy);
}
}
static void nfs4_put_copy(struct nfsd4_copy *copy)
static void nfs4_put_copy(struct nfsd4_async_copy *copy)
{
if (!refcount_dec_and_test(&copy->refcount))
return;
kfree(copy->cp_src);
kfree(copy);
/*
* Drop the copy offload stateid's sole reference: removes it from
* cl_stateids and frees the async_copy via nfsd4_free_async_copy_stid().
*/
nfs4_put_stid(&copy->cp_stid);
}
static void release_copy_files(struct nfsd4_copy *copy);
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
static void nfsd4_stop_copy(struct nfsd4_async_copy *copy)
{
trace_nfsd_copy_async_cancel(copy);
if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) {
kthread_stop(copy->copy_task);
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
copy->nfserr = nfs_ok;
set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
}
trace_nfsd_copy_async_cancel(&copy->cp_copy);
/*
* The copy was removed from async_copies before this function
* was called, so the reaper cannot clean it up. Release files
* here regardless of who won the STOPPED race. If the thread
* set STOPPED, it has finished using the files. If STOPPED
* was set here, kthread_stop() waited for the thread to exit.
* Join the kthread before releasing its resources. The task_struct is
* pinned in nfsd4_copy(), so kthread_stop() is safe even after the
* one-shot kthread has exited. The caller already unlinked the copy,
* so this runs once per copy.
*/
release_copy_files(copy);
set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_copy.cp_flags);
kthread_stop(copy->copy_task);
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_copy.cp_flags))
copy->cp_copy.nfserr = nfs_ok;
set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_copy.cp_flags);
release_copy_files(&copy->cp_copy);
nfs4_put_copy(copy);
}
static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp)
static struct nfsd4_async_copy *nfsd4_unhash_copy(struct nfs4_client *clp)
{
struct nfsd4_copy *copy = NULL;
struct nfsd4_async_copy *copy = NULL;
spin_lock(&clp->async_lock);
if (!list_empty(&clp->async_copies)) {
copy = list_first_entry(&clp->async_copies, struct nfsd4_copy,
copies);
copy = list_first_entry(&clp->async_copies,
struct nfsd4_async_copy, copies);
refcount_inc(&copy->refcount);
copy->cp_clp = NULL;
/* Pairs with smp_load_acquire() in nfsd4_send_cb_offload(). */
smp_store_release(&copy->cp_copy.cp_clp, NULL);
if (!list_empty(&copy->copies))
list_del_init(&copy->copies);
}
@@ -1540,10 +1570,13 @@ static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp)
void nfsd4_shutdown_copy(struct nfs4_client *clp)
{
struct nfsd4_copy *copy;
struct nfsd4_async_copy *copy;
while ((copy = nfsd4_unhash_copy(clp)) != NULL)
while ((copy = nfsd4_unhash_copy(clp)) != NULL) {
nfsd4_stop_copy(copy);
/* Reaper can't reach the unhashed copy; drop its membership ref. */
nfs4_put_copy(copy);
}
}
static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy,
@@ -1562,15 +1595,20 @@ static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy,
* nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb
* @net: net namespace containing the copy operations
* @sb: targeted superblock
*
* Context: Caller must hold nfsd_mutex with NFSD_NET_UP set. Outside
* that window nn->conf_id_hashtbl is unallocated or freed,
* so the walk would dereference a NULL or dangling pointer.
*/
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct nfsd4_copy *copy, *tmp;
struct nfsd4_async_copy *copy, *tmp;
struct nfs4_client *clp;
unsigned int idhashval;
LIST_HEAD(to_cancel);
lockdep_assert_held(&nfsd_mutex);
spin_lock(&nn->client_lock);
for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
struct list_head *head = &nn->conf_id_hashtbl[idhashval];
@@ -1579,7 +1617,7 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
spin_lock(&clp->async_lock);
list_for_each_entry_safe(copy, tmp,
&clp->async_copies, copies) {
if (nfsd4_copy_on_sb(copy, sb)) {
if (nfsd4_copy_on_sb(&copy->cp_copy, sb)) {
refcount_inc(&copy->refcount);
/*
* Hold a reference on the client while
@@ -1591,9 +1629,9 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
* survive callback flight.
*/
kref_get(&clp->cl_nfsdfs.cl_ref);
copy->nfserr = nfserr_admin_revoked;
copy->cp_copy.nfserr = nfserr_admin_revoked;
set_bit(NFSD4_COPY_F_CB_ERROR,
&copy->cp_flags);
&copy->cp_copy.cp_flags);
list_move(&copy->copies, &to_cancel);
}
}
@@ -1603,10 +1641,12 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
spin_unlock(&nn->client_lock);
list_for_each_entry_safe(copy, tmp, &to_cancel, copies) {
struct nfs4_client *clp = copy->cp_clp;
struct nfs4_client *clp = copy->cp_copy.cp_clp;
list_del_init(&copy->copies);
nfsd4_stop_copy(copy);
/* Reaper can't reach the unlinked copy; drop the membership ref here. */
nfs4_put_copy(copy);
nfsd4_put_client(clp);
}
}
@@ -1896,11 +1936,14 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
{
struct nfsd4_cb_offload *cbo =
container_of(cb, struct nfsd4_cb_offload, co_cb);
struct nfsd4_copy *copy =
container_of(cbo, struct nfsd4_copy, cp_cb_offload);
struct nfsd4_async_copy *copy =
container_of(cbo, struct nfsd4_async_copy, cp_cb_offload);
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_copy.cp_flags);
/* nfs4_put_copy needs clp; cl_cb_inflight defers free_client. */
nfsd4_put_client(cb->cb_clp);
/* Drop the copy reference taken in nfsd4_send_cb_offload(). */
nfs4_put_copy(copy);
}
static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
@@ -1950,6 +1993,7 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
/* See RFC 7862 p.67: */
if (bytes_total == 0)
bytes_total = ULLONG_MAX;
since = READ_ONCE(dst->f_wb_err);
do {
/* Only async copies can be stopped here */
if (kthread_should_stop())
@@ -1965,13 +2009,14 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
} while (bytes_total > 0 && nfsd4_copy_is_async(copy));
/* for a non-zero asynchronous copy do a commit of data */
if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) {
since = READ_ONCE(dst->f_wb_err);
end = copy->cp_dst_pos + copy->cp_res.wr_bytes_written - 1;
status = vfs_fsync_range(dst, copy->cp_dst_pos, end, 0);
if (!status)
status = filemap_check_wb_err(dst->f_mapping, since);
if (!status)
set_bit(NFSD4_COPY_F_COMMITTED, &copy->cp_flags);
else if (status != -EAGAIN && status != -ESTALE)
nfsd_reset_write_verifier(copy->cp_nn);
}
return bytes_copied;
}
@@ -2011,7 +2056,6 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
if (!nfsd4_ssc_is_inter(src))
dst->nf_src = nfsd_file_get(src->nf_src);
memcpy(&dst->cp_stateid, &src->cp_stateid, sizeof(src->cp_stateid));
memcpy(dst->cp_src, src->cp_src, sizeof(struct nl4_server));
memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid));
memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh));
@@ -2030,43 +2074,45 @@ static void release_copy_files(struct nfsd4_copy *copy)
}
}
static void cleanup_async_copy(struct nfsd4_copy *copy)
/*
* Called from the reaper and from nfsd4_copy()'s error path; in both
* cases the copy is already unreachable from clp->async_copies.
*/
static void cleanup_async_copy(struct nfsd4_async_copy *copy)
{
nfs4_free_copy_state(copy);
release_copy_files(copy);
if (copy->cp_clp) {
spin_lock(&copy->cp_clp->async_lock);
if (!list_empty(&copy->copies))
list_del_init(&copy->copies);
spin_unlock(&copy->cp_clp->async_lock);
}
release_copy_files(&copy->cp_copy);
nfs4_put_copy(copy);
}
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
static void nfsd4_send_cb_offload(struct nfsd4_async_copy *copy)
{
struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload;
struct nfs4_client *clp = copy->cp_clp;
struct nfs4_client *clp;
/*
* cp_clp is NULL when called via nfsd4_shutdown_copy() during
* client destruction. Skip the callback; the client is gone.
* Pairs with smp_store_release(&cp_clp) in find_async_copy() and
* nfsd4_unhash_copy(); the set_bit/clear_bit writers are unordered.
* cp_clp is NULL once the copy was canceled; skip the callback, the
* canceling path owns the notification.
*/
clp = smp_load_acquire(&copy->cp_copy.cp_clp);
if (!clp) {
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_copy.cp_flags);
return;
}
memcpy(&cbo->co_res, &copy->cp_res, sizeof(copy->cp_res));
memcpy(&cbo->co_fh, &copy->fh, sizeof(copy->fh));
cbo->co_nfserr = copy->nfserr;
memcpy(&cbo->co_res, &copy->cp_copy.cp_res, sizeof(copy->cp_copy.cp_res));
memcpy(&cbo->co_fh, &copy->cp_copy.fh, sizeof(copy->cp_copy.fh));
cbo->co_nfserr = copy->cp_copy.nfserr;
cbo->co_retries = 5;
/*
* Hold a reference on the client while the callback is in flight.
* Released in nfsd4_cb_offload_release().
* Hold the client and the copy across the in-flight callback; co_cb is
* embedded in the copy, so it must outlive the callback. Both are
* dropped in nfsd4_cb_offload_release().
*/
kref_get(&clp->cl_nfsdfs.cl_ref);
refcount_inc(&copy->refcount);
nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops,
NFSPROC4_CLNT_CB_OFFLOAD);
@@ -2074,7 +2120,8 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
cbo->co_referring_slotid,
cbo->co_referring_seqno);
trace_nfsd_cb_offload(clp, &cbo->co_res.cb_stateid,
&cbo->co_fh, copy->cp_count, copy->nfserr);
&cbo->co_fh, copy->cp_copy.cp_count,
copy->cp_copy.nfserr);
nfsd4_try_run_cb(&cbo->co_cb);
}
@@ -2087,7 +2134,8 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
*/
static int nfsd4_do_async_copy(void *data)
{
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
struct nfsd4_async_copy *async = data;
struct nfsd4_copy *copy = &async->cp_copy;
__be32 nfserr = nfs_ok;
trace_nfsd_copy_async(copy);
@@ -2118,16 +2166,20 @@ static int nfsd4_do_async_copy(void *data)
do_callback:
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
copy->nfserr = nfserr;
/* The kthread exits forthwith. Ensure that a subsequent
* OFFLOAD_CANCEL won't try to kill it again. */
set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags);
/*
* Don't set NFSD4_COPY_F_STOPPED here: it tells a teardown caller it
* may skip kthread_stop(), which would then release nf_dst and the
* client while still in use. Only nfsd4_stop_copy() sets it, after
* joining.
*/
set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
trace_nfsd_copy_async_done(copy);
atomic_dec(&copy->cp_nn->pending_async_copies);
if (copy->cp_res.wr_bytes_written > 0 && copy->attr_update)
nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0);
nfsd4_send_cb_offload(copy);
nfsd4_send_cb_offload(async);
/* Drop the kthread's reference (taken in nfsd4_copy()); copy may be freed after this. */
nfs4_put_copy(async);
return 0;
}
@@ -2136,7 +2188,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfsd4_copy *async_copy = NULL;
struct nfsd4_async_copy *async_copy = NULL;
struct nfsd4_copy *copy = &u->copy;
struct nfsd42_write_res *result;
__be32 status;
@@ -2153,25 +2205,30 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
status = nfsd4_setup_inter_ssc(rqstp, cstate, copy);
if (status) {
trace_nfsd_copy_done(copy, status);
return nfserr_offload_denied;
status = nfserr_offload_denied;
goto out;
}
} else {
trace_nfsd_copy_intra(copy);
status = nfsd4_setup_intra_ssc(rqstp, cstate, copy);
if (status) {
trace_nfsd_copy_done(copy, status);
return status;
}
if (status)
goto out;
}
memcpy(&copy->fh, &cstate->current_fh.fh_handle,
sizeof(struct knfsd_fh));
if (nfsd4_copy_is_async(copy)) {
async_copy = kzalloc_obj(struct nfsd4_copy);
struct task_struct *task;
/*
* Allocate the durable async copy. Its offload stateid is a
* first-class nfs4_stid in clp->cl_stateids, returned to the
* client and freed only when the background copy is torn down.
*/
async_copy = nfs4_alloc_copy_stid(cstate->clp);
if (!async_copy)
goto out_err;
async_copy->cp_nn = nn;
async_copy->cp_copy.cp_nn = nn;
INIT_LIST_HEAD(&async_copy->copies);
refcount_set(&async_copy->refcount, 1);
async_copy->cp_ttl = NFSD_COPY_INITIAL_TTL;
@@ -2179,31 +2236,45 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (atomic_inc_return(&nn->pending_async_copies) >
(int)rqstp->rq_pool->sp_nrthreads)
goto out_dec_async_copy_err;
async_copy->cp_src = kmalloc_obj(*async_copy->cp_src);
if (!async_copy->cp_src)
async_copy->cp_copy.cp_src = kmalloc_obj(*async_copy->cp_copy.cp_src);
if (!async_copy->cp_copy.cp_src)
goto out_dec_async_copy_err;
if (!nfs4_init_copy_state(nn, copy))
goto out_dec_async_copy_err;
memcpy(&result->cb_stateid, &copy->cp_stateid.cs_stid,
memcpy(&result->cb_stateid, &async_copy->cp_stid.sc_stateid,
sizeof(result->cb_stateid));
dup_copy_fields(copy, async_copy);
/*
* dup after writing cb_stateid; duplicating first would leave
* the callback stateid zeroed.
*/
dup_copy_fields(copy, &async_copy->cp_copy);
if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) &
FMODE_NOCMTIME) != 0)
async_copy->attr_update = true;
async_copy->cp_copy.attr_update = true;
memcpy(async_copy->cp_cb_offload.co_referring_sessionid.data,
cstate->session->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);
async_copy->cp_cb_offload.co_referring_slotid = cstate->slot->sl_index;
async_copy->cp_cb_offload.co_referring_seqno = cstate->slot->sl_seqid;
async_copy->copy_task = kthread_create(nfsd4_do_async_copy,
async_copy, "%s", "copy thread");
if (IS_ERR(async_copy->copy_task))
task = kthread_create(nfsd4_do_async_copy, async_copy,
"%s", "copy thread");
if (IS_ERR(task))
goto out_dec_async_copy_err;
spin_lock(&async_copy->cp_clp->async_lock);
list_add(&async_copy->copies,
&async_copy->cp_clp->async_copies);
spin_unlock(&async_copy->cp_clp->async_lock);
/*
* Pin the task_struct so kthread_stop() is safe after this
* one-shot kthread exits. Released by nfs4_put_copy().
*/
get_task_struct(task);
async_copy->copy_task = task;
/*
* Take the kthread's ref and wake it before publishing, so the
* publisher touches async_copy no further and teardown can
* drain it.
*/
refcount_inc(&async_copy->refcount);
wake_up_process(async_copy->copy_task);
spin_lock(&async_copy->cp_copy.cp_clp->async_lock);
list_add(&async_copy->copies,
&async_copy->cp_copy.cp_clp->async_copies);
spin_unlock(&async_copy->cp_copy.cp_clp->async_lock);
status = nfs_ok;
} else {
status = nfsd4_do_copy(copy, copy->nf_src->nf_file,
@@ -2235,30 +2306,39 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
}
static struct nfsd4_copy *
static struct nfsd4_async_copy *
find_async_copy_locked(struct nfs4_client *clp, stateid_t *stateid)
{
struct nfsd4_copy *copy;
struct nfsd4_async_copy *copy;
lockdep_assert_held(&clp->async_lock);
list_for_each_entry(copy, &clp->async_copies, copies) {
if (memcmp(&copy->cp_stateid.cs_stid, stateid, NFS4_STATEID_SIZE))
if (memcmp(&copy->cp_stid.sc_stateid, stateid, NFS4_STATEID_SIZE))
continue;
return copy;
}
return NULL;
}
static struct nfsd4_copy *
static struct nfsd4_async_copy *
find_async_copy(struct nfs4_client *clp, stateid_t *stateid)
{
struct nfsd4_copy *copy;
struct nfsd4_async_copy *copy;
spin_lock(&clp->async_lock);
copy = find_async_copy_locked(clp, stateid);
if (copy)
if (copy) {
refcount_inc(&copy->refcount);
/*
* Mirror nfsd4_unhash_copy(): unlink and clear cp_clp under
* async_lock so the reaper can't reach it. Caller drops the
* membership ref after nfsd4_stop_copy().
*/
smp_store_release(&copy->cp_copy.cp_clp, NULL);
if (!list_empty(&copy->copies))
list_del_init(&copy->copies);
}
spin_unlock(&clp->async_lock);
return copy;
}
@@ -2269,7 +2349,7 @@ nfsd4_offload_cancel(struct svc_rqst *rqstp,
union nfsd4_op_u *u)
{
struct nfsd4_offload_status *os = &u->offload_status;
struct nfsd4_copy *copy;
struct nfsd4_async_copy *copy;
struct nfs4_client *clp = cstate->clp;
copy = find_async_copy(clp, &os->stateid);
@@ -2277,12 +2357,46 @@ nfsd4_offload_cancel(struct svc_rqst *rqstp,
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
return manage_cpntf_state(nn, &os->stateid, clp, NULL);
} else
} else {
nfsd4_stop_copy(copy);
/* find_async_copy() unlinked it from the reaper; drop the membership ref. */
nfs4_put_copy(copy);
}
return nfs_ok;
}
static __be32
nfsd4_set_netaddr(struct sockaddr *addr, struct nfs42_netaddr *netaddr)
{
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
unsigned int port;
size_t ret_addr, ret_port;
switch (addr->sa_family) {
case AF_INET:
port = ntohs(sin->sin_port);
sprintf(netaddr->netid, "tcp");
netaddr->netid_len = 3;
break;
case AF_INET6:
port = ntohs(sin6->sin6_port);
sprintf(netaddr->netid, "tcp6");
netaddr->netid_len = 4;
break;
default:
return nfserr_inval;
}
ret_addr = rpc_ntop(addr, netaddr->addr, sizeof(netaddr->addr));
ret_port = snprintf(netaddr->addr + ret_addr,
RPCBIND_MAXUADDRLEN + 1 - ret_addr,
".%u.%u", port >> 8, port & 0xff);
WARN_ON(ret_port >= RPCBIND_MAXUADDRLEN + 1 - ret_addr);
netaddr->addr_len = ret_addr + ret_port;
return 0;
}
static __be32
nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
@@ -2292,7 +2406,6 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfs4_stid *stid = NULL;
struct nfs4_cpntf_state *cps;
struct nfs4_client *clp = cstate->clp;
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&cn->cpn_src_stateid, RD_STATE, NULL,
@@ -2306,12 +2419,14 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
cn->cpn_lease_time.tv_nsec = 0;
status = nfserrno(-ENOMEM);
/*
* The returned cps is published and fully initialized, and carries an
* extra reference for us; drop it once we are done with it.
*/
cps = nfs4_alloc_init_cpntf_state(nn, stid);
if (!cps)
goto out;
memcpy(&cn->cpn_cnr_stateid, &cps->cp_stateid.cs_stid, sizeof(stateid_t));
memcpy(&cps->cp_p_stateid, &stid->sc_stateid, sizeof(stateid_t));
memcpy(&cps->cp_p_clid, &clp->cl_clientid, sizeof(clientid_t));
/* For now, only return one server address in cpn_src, the
* address used by the client to connect to this server.
@@ -2320,10 +2435,11 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr,
&cn->cpn_src->u.nl4_addr);
WARN_ON_ONCE(status);
if (status) {
nfs4_put_cpntf_state(nn, cps);
goto out;
}
/*
* Drop our extra reference. The membership reference keeps the entry
* alive for a later inter-server READ, or until the laundromat reaps it.
*/
nfs4_put_cpntf_state(nn, cps);
out:
nfs4_put_stid(stid);
return status;
@@ -2357,17 +2473,17 @@ nfsd4_offload_status(struct svc_rqst *rqstp,
{
struct nfsd4_offload_status *os = &u->offload_status;
__be32 status = nfs_ok;
struct nfsd4_copy *copy;
struct nfsd4_async_copy *copy;
struct nfs4_client *clp = cstate->clp;
os->completed = false;
spin_lock(&clp->async_lock);
copy = find_async_copy_locked(clp, &os->stateid);
if (copy) {
os->count = copy->cp_res.wr_bytes_written;
if (test_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags)) {
os->count = copy->cp_copy.cp_res.wr_bytes_written;
if (test_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_copy.cp_flags)) {
os->completed = true;
os->status = copy->nfserr;
os->status = copy->cp_copy.nfserr;
}
} else
status = nfserr_bad_stateid;
@@ -2521,12 +2637,19 @@ nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status == nfserr_same ? nfs_ok : status;
}
#define SUPPORTED_NOTIFY_MASK (BIT(NOTIFY4_CHANGE_DIR_ATTRS) | \
BIT(NOTIFY4_REMOVE_ENTRY) | \
BIT(NOTIFY4_ADD_ENTRY) | \
BIT(NOTIFY4_RENAME_ENTRY) | \
BIT(NOTIFY4_GFLAG_EXTEND))
static __be32
nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
u32 requested = gdd->gdda_notification_types[0];
struct nfs4_delegation *dd;
struct nfsd_file *nf;
__be32 status;
@@ -2535,6 +2658,21 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
if (status != nfs_ok)
return status;
/*
* Offer no notifications to an order-aware client. RFC8881bis section
* 16.2.13 defines order-aware as NOTIFY4_CFLAG_ORDER being set or
* NOTIFY4_GFLAG_EXTEND being reset. Such a client expects cookie and
* previous-entry information with its notifications (e.g. 27.4.5), and
* nfsd does not track or emit directory offset information. Per
* 16.2.11.3 the alternative would be to recall the delegation, so it's
* simpler to just decline the notifications here.
*/
if (!(requested & BIT(NOTIFY4_GFLAG_EXTEND)) ||
(requested & BIT(NOTIFY4_CFLAG_ORDER)))
requested = 0;
gdd->gddr_notification[0] = requested & SUPPORTED_NOTIFY_MASK;
/*
* RFC 8881, section 18.39.3 says:
*
@@ -2556,6 +2694,10 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
gdd->gddrnf_status = GDD4_OK;
memcpy(&gdd->gddr_stateid, &dd->dl_stid.sc_stateid, sizeof(gdd->gddr_stateid));
gdd->gddr_child_attributes[0] = dd->dl_child_attrs[0];
gdd->gddr_child_attributes[1] = dd->dl_child_attrs[1];
gdd->gddr_dir_attributes[0] = dd->dl_dir_attrs[0];
gdd->gddr_dir_attributes[1] = dd->dl_dir_attrs[1];
nfs4_put_stid(&dd->dl_stid);
return nfs_ok;
}
@@ -3119,9 +3261,22 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
op->status = nfsd4_open_omfg(rqstp, cstate, op);
goto encode_op;
}
if (!current_fh->fh_dentry &&
!HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
if (!current_fh->fh_dentry) {
if (HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
/*
* FOREIGN fh from inter-SSC PUTFH: only
* SAVEFH may proceed with a NULL fh_dentry.
* Per RFC 7862 S15.2.3, validation of a
* foreign fh is deferred to the operation
* that consumes it, and NFS4ERR_STALE is
* returned at that point.
*/
if (op->opnum != OP_SAVEFH &&
!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
op->status = nfserr_stale;
goto encode_op;
}
} else if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
op->status = nfserr_nofilehandle;
goto encode_op;
}
@@ -3185,6 +3340,9 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
status = op->status;
}
if (op->opdesc && op->opdesc->op_release)
op->opdesc->op_release(&op->u);
trace_nfsd_compound_status(args->client_opcnt, resp->opcnt,
status, nfsd4_op_name(op->opnum));
@@ -3506,8 +3664,8 @@ static u32 nfsd4_get_dir_delegation_rsize(const struct svc_rqst *rqstp,
op_encode_verifier_maxsz +
op_encode_stateid_maxsz +
2 /* gddr_notification */ +
2 /* gddr_child_attributes */ +
2 /* gddr_dir_attributes */);
3 /* gddr_child_attributes */ +
3 /* gddr_dir_attributes */) * sizeof(__be32);
}
#ifdef CONFIG_NFSD_PNFS
@@ -3684,6 +3842,7 @@ static const struct nfsd4_operation nfsd4_ops[] = {
},
[OP_OPEN] = {
.op_func = nfsd4_open,
.op_release = nfsd4_open_release,
.op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
.op_name = "OP_OPEN",
.op_rsize_bop = nfsd4_open_rsize,
@@ -4103,13 +4262,10 @@ static const struct svc_procedure nfsd_procedures4[2] = {
},
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nfsd_count4[ARRAY_SIZE(nfsd_procedures4)]);
const struct svc_version nfsd_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nfsd_procedures4),
.vs_proc = nfsd_procedures4,
.vs_count = nfsd_count4,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS4_SVC_XDRSIZE,
.vs_rpcb_optnl = true,

View File

@@ -167,7 +167,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
end_creating(dentry);
out:
if (status == 0) {
if (nn->in_grace)
if (test_bit(NFSD_NET_IN_GRACE, &nn->flags))
__nfsd4_create_reclaim_record_grace(clp, dname, nn);
vfs_fsync(nn->rec_file, 0);
} else {
@@ -285,10 +285,12 @@ __nfsd4_remove_reclaim_record_grace(const char *dname, int len,
return;
}
name.len = len;
down_write(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(name, nn);
kfree(name.data);
if (crp)
nfs4_remove_reclaim_record(crp, nn);
up_write(&nn->reclaim_str_hashtbl_lock);
kfree(name.data);
}
static void
@@ -317,7 +319,7 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
nfs4_reset_creds(original_cred);
if (status == 0) {
vfs_fsync(nn->rec_file, 0);
if (nn->in_grace)
if (test_bit(NFSD_NET_IN_GRACE, &nn->flags))
__nfsd4_remove_reclaim_record_grace(dname,
HEXDIR_LEN, nn);
}
@@ -373,7 +375,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn)
{
int status;
nn->in_grace = false;
clear_bit(NFSD_NET_IN_GRACE, &nn->flags);
if (!nn->rec_file)
return;
status = mnt_want_write_file(nn->rec_file);
@@ -455,7 +457,7 @@ nfsd4_init_recdir(struct net *net)
nfs4_reset_creds(original_cred);
if (!status)
nn->in_grace = true;
set_bit(NFSD_NET_IN_GRACE, &nn->flags);
return status;
}
@@ -484,6 +486,7 @@ nfs4_legacy_state_init(struct net *net)
for (i = 0; i < CLIENT_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
nn->reclaim_str_hashtbl_size = 0;
init_rwsem(&nn->reclaim_str_hashtbl_lock);
return 0;
}
@@ -598,13 +601,16 @@ nfsd4_check_legacy_client(struct nfs4_client *clp)
goto out_enoent;
}
name.len = HEXDIR_LEN;
down_read(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(name, nn);
kfree(name.data);
if (crp) {
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
crp->cr_clp = clp;
return 0;
}
up_read(&nn->reclaim_str_hashtbl_lock);
kfree(name.data);
if (crp)
return 0;
out_enoent:
return -ENOENT;
@@ -1176,6 +1182,7 @@ nfsd4_cld_check(struct nfs4_client *clp)
return 0;
/* look for it in the reclaim hashtable otherwise */
down_read(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
if (crp)
goto found;
@@ -1191,6 +1198,7 @@ nfsd4_cld_check(struct nfs4_client *clp)
if (!name.data) {
dprintk("%s: failed to allocate memory for name.data!\n",
__func__);
up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
}
name.len = HEXDIR_LEN;
@@ -1201,9 +1209,11 @@ nfsd4_cld_check(struct nfs4_client *clp)
}
#endif
up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
found:
crp->cr_clp = clp;
up_read(&nn->reclaim_str_hashtbl_lock);
return 0;
}
@@ -1215,6 +1225,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
struct cld_net *cn = nn->cld_net;
#endif
struct nfs4_client_reclaim *crp;
unsigned int princhashlen;
char *principal = NULL;
/* did we already find that this client is stable? */
@@ -1222,6 +1233,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
return 0;
/* look for it in the reclaim hashtable otherwise */
down_read(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
if (crp)
goto found;
@@ -1237,6 +1249,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
if (!name.data) {
dprintk("%s: failed to allocate memory for name.data\n",
__func__);
up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
}
name.len = HEXDIR_LEN;
@@ -1247,23 +1260,31 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
}
#endif
up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
found:
if (crp->cr_princhash.len) {
princhashlen = crp->cr_princhash.len;
if (princhashlen) {
u8 digest[SHA256_DIGEST_SIZE];
u8 *pdata;
if (clp->cl_cred.cr_raw_principal)
principal = clp->cl_cred.cr_raw_principal;
else if (clp->cl_cred.cr_principal)
principal = clp->cl_cred.cr_principal;
if (principal == NULL)
if (principal == NULL) {
up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
}
sha256(principal, strlen(principal), digest);
if (memcmp(crp->cr_princhash.data, digest,
crp->cr_princhash.len))
pdata = crp->cr_princhash.data;
if (memcmp(pdata, digest, princhashlen)) {
up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
}
}
crp->cr_clp = clp;
up_read(&nn->reclaim_str_hashtbl_lock);
return 0;
}
@@ -1362,7 +1383,8 @@ nfs4_cld_state_init(struct net *net)
for (i = 0; i < CLIENT_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
nn->reclaim_str_hashtbl_size = 0;
nn->track_reclaim_completes = true;
init_rwsem(&nn->reclaim_str_hashtbl_lock);
set_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags);
atomic_set(&nn->nr_reclaim_complete, 0);
return 0;
@@ -1373,7 +1395,7 @@ nfs4_cld_state_shutdown(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nn->track_reclaim_completes = false;
clear_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags);
kfree(nn->reclaim_str_hashtbl);
}

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,8 @@
#include <uapi/linux/xattr.h>
#include "attr4.h"
#include "auth.h"
#include "idmap.h"
#include "acl.h"
#include "xdr4.h"
@@ -98,7 +100,7 @@ check_filename(char *str, int len)
return nfserr_inval;
if (len > NFS4_MAXNAMLEN)
return nfserr_nametoolong;
if (isdotent(str, len))
if (name_is_dot_dotdot(str, len))
return nfserr_badname;
for (i = 0; i < len; i++)
if (str[i] == '/')
@@ -244,7 +246,7 @@ nfsd4_decode_nfstime4(struct nfsd4_compoundargs *argp, struct timespec64 *tv)
return nfserr_bad_xdr;
p = xdr_decode_hyper(p, &tv->tv_sec);
tv->tv_nsec = be32_to_cpup(p++);
if (tv->tv_nsec >= (u32)1000000000)
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return nfserr_inval;
return nfs_ok;
}
@@ -449,9 +451,18 @@ nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl)
if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
return nfserr_bad_xdr;
/*
* The NFSv4 POSIX ACL draft doesn't define a max number of ACE's, but
* the NFSACL spec does. For NFSv4, cap the number of entries to the v3
* limit, as we want to ensure that ACLs set via NFSv4 POSIX ACL
* extensions are retrievable via NFSACL.
*/
if (count > NFS_ACL_MAX_ENTRIES)
return nfserr_inval;
*acl = posix_acl_alloc(count, GFP_KERNEL);
if (*acl == NULL)
return nfserr_resource;
return nfserr_jukebox;
(*acl)->a_count = count;
for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) {
@@ -628,6 +639,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
if (!xdrgen_decode_fattr4_time_deleg_access(argp->xdr, &access))
return nfserr_bad_xdr;
if (access.nseconds >= NSEC_PER_SEC)
return nfserr_inval;
iattr->ia_atime.tv_sec = access.seconds;
iattr->ia_atime.tv_nsec = access.nseconds;
iattr->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET | ATTR_DELEG;
@@ -637,6 +650,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
if (!xdrgen_decode_fattr4_time_deleg_modify(argp->xdr, &modify))
return nfserr_bad_xdr;
if (modify.nseconds >= NSEC_PER_SEC)
return nfserr_inval;
iattr->ia_mtime.tv_sec = modify.seconds;
iattr->ia_mtime.tv_nsec = modify.nseconds;
iattr->ia_ctime.tv_sec = modify.seconds;
@@ -955,6 +970,10 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
case NF4LNK:
if (xdr_stream_decode_u32(argp->xdr, &create->cr_datalen) < 0)
return nfserr_bad_xdr;
if (create->cr_datalen == 0)
return nfserr_inval;
if (create->cr_datalen > NFS4_MAXPATHLEN)
return nfserr_nametoolong;
p = xdr_inline_decode(argp->xdr, create->cr_datalen);
if (!p)
return nfserr_bad_xdr;
@@ -2106,6 +2125,7 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
{
struct nfs42_netaddr *naddr;
__be32 *p;
u32 str_len;
if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0)
return nfserr_bad_xdr;
@@ -2135,6 +2155,18 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
return nfserr_bad_xdr;
memcpy(naddr->addr, p, naddr->addr_len);
break;
case NL4_NAME:
case NL4_URL:
/*
* Well-formed XDR, but only NL4_NETADDR is supported. Consume
* the utf8str_cis to keep the stream aligned, then return
* NFS4ERR_NOTSUPP rather than the misleading NFS4ERR_BADXDR.
*/
if (xdr_stream_decode_u32(argp->xdr, &str_len) < 0)
return nfserr_bad_xdr;
if (!xdr_inline_decode(argp->xdr, str_len))
return nfserr_bad_xdr;
return nfserr_notsupp;
default:
return nfserr_bad_xdr;
}
@@ -2702,7 +2734,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
}
static __be32 nfsd4_encode_nfs_fh4(struct xdr_stream *xdr,
struct knfsd_fh *fh_handle)
const struct knfsd_fh *fh_handle)
{
return nfsd4_encode_opaque(xdr, fh_handle->fh_raw, fh_handle->fh_size);
}
@@ -3142,9 +3174,9 @@ nfsd4_encode_bitmap4(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2)
struct nfsd4_fattr_args {
struct svc_rqst *rqstp;
struct svc_fh *fhp;
struct svc_export *exp;
struct dentry *dentry;
struct knfsd_fh fhandle;
struct kstat stat;
struct kstatfs statfs;
struct nfs4_acl *acl;
@@ -3260,7 +3292,7 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
{
const struct svc_export *exp = args->exp;
if (unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
if (exp && unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
u32 flush_time = convert_to_wallclock(exp->cd->flush_time);
if (xdr_stream_encode_u32(xdr, flush_time) != XDR_UNIT)
@@ -3292,7 +3324,7 @@ static __be32 nfsd4_encode_fattr4_fsid(struct xdr_stream *xdr,
xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
return nfs_ok;
}
switch (fsid_source(args->fhp)) {
switch (fsid_source_fh(&args->fhandle, args->exp)) {
case FSIDSOURCE_FSID:
p = xdr_encode_hyper(p, (u64)args->exp->ex_fsid);
xdr_encode_hyper(p, (u64)0);
@@ -3389,7 +3421,7 @@ static __be32 nfsd4_encode_fattr4_homogeneous(struct xdr_stream *xdr,
static __be32 nfsd4_encode_fattr4_filehandle(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
return nfsd4_encode_nfs_fh4(xdr, &args->fhp->fh_handle);
return nfsd4_encode_nfs_fh4(xdr, &args->fhandle);
}
static __be32 nfsd4_encode_fattr4_fileid(struct xdr_stream *xdr,
@@ -3882,6 +3914,22 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
#endif
};
static __be32
nfsd4_encode_attr_vals(struct xdr_stream *xdr, u32 *attrmask, struct nfsd4_fattr_args *args)
{
DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
unsigned long bit;
__be32 status;
bitmap_from_arr32(attr_bitmap, attrmask, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
for_each_set_bit(bit, attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)) {
status = nfsd4_enc_fattr4_encode_ops[bit](xdr, args);
if (status != nfs_ok)
return status;
}
return nfs_ok;
}
/*
* Note: @fhp can be NULL; in this case, we might have to compose the filehandle
* ourselves. @case_cache is NULL for callers that encode a single dentry
@@ -3895,7 +3943,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
int ignore_crossmnt,
struct nfsd_case_attrs_cache *case_cache)
{
DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
struct nfs4_delegation *dp = NULL;
struct nfsd4_fattr_args args;
struct svc_fh *tempfh = NULL;
@@ -3910,7 +3957,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
.mnt = exp->ex_path.mnt,
.dentry = dentry,
};
unsigned long bit;
WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1);
WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval));
@@ -3988,19 +4034,22 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
if (err)
goto out_nfserr;
}
if ((attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) &&
!fhp) {
tempfh = kmalloc_obj(struct svc_fh);
status = nfserr_jukebox;
if (!tempfh)
goto out;
fh_init(tempfh, NFS4_FHSIZE);
status = fh_compose(tempfh, exp, dentry, NULL);
if (status)
goto out;
args.fhp = tempfh;
} else
args.fhp = fhp;
if ((attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID))) {
if (!fhp) {
tempfh = kmalloc_obj(struct svc_fh);
status = nfserr_jukebox;
if (!tempfh)
goto out;
fh_init(tempfh, NFS4_FHSIZE);
status = fh_compose(tempfh, exp, dentry, NULL);
if (status)
goto out;
fhp = tempfh;
}
fh_copy_shallow(&args.fhandle, &fhp->fh_handle);
}
if (attrmask[0] & (FATTR4_WORD0_CASE_INSENSITIVE |
FATTR4_WORD0_CASE_PRESERVING)) {
/*
@@ -4124,27 +4173,22 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
/* attrmask */
status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1],
attrmask[2]);
status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1], attrmask[2]);
if (status)
goto out;
/* attr_vals */
attrlen_offset = xdr->buf->len;
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
goto out_resource;
bitmap_from_arr32(attr_bitmap, attrmask,
ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
for_each_set_bit(bit, attr_bitmap,
ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)) {
status = nfsd4_enc_fattr4_encode_ops[bit](xdr, &args);
if (status != nfs_ok)
goto out;
if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT))) {
status = nfserr_resource;
goto out;
}
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;
status = nfsd4_encode_attr_vals(xdr, attrmask, &args);
if (status == nfs_ok) {
attrlen = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, XDR_UNIT);
}
out:
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
if (args.dpacl)
@@ -4167,9 +4211,286 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
out_nfserr:
status = nfserrno(err);
goto out;
out_resource:
status = nfserr_resource;
goto out;
}
static bool
setup_notify_fhandle(struct dentry *dentry, struct nfs4_delegation *dp,
struct nfsd_file *nf, struct nfsd4_fattr_args *args)
{
struct nfs4_file *fi = dp->dl_stid.sc_file;
struct nfs4_client *clp = dp->dl_stid.sc_client;
int fileid_type, fsid_len, maxsize, flags = 0;
struct knfsd_fh *fhp = &args->fhandle;
struct inode *inode = d_inode(dentry);
struct inode *parent = NULL;
struct svc_export *exp;
struct fid *fid;
bool ret = false;
/*
* drop_stid_export() can clear sc_export under cl_lock and drop its
* reference when the delegation is admin-revoked, concurrently with
* this callback. Grab our own reference under cl_lock so the export
* can be neither NULL-raced nor freed while we encode.
*/
spin_lock(&clp->cl_lock);
exp = dp->dl_stid.sc_export;
if (exp)
exp_get(exp);
spin_unlock(&clp->cl_lock);
fsid_len = key_len(fi->fi_fhandle.fh_fsid_type);
fhp->fh_size = 4 + fsid_len;
/* Copy first 4 bytes + fsid */
memcpy(&fhp->fh_raw, &fi->fi_fhandle.fh_raw, fhp->fh_size);
fid = (struct fid *)(fh_fsid(fhp) + fsid_len/4);
maxsize = (NFS4_FHSIZE - fhp->fh_size)/4;
/*
* Subtree-checking exports need a connectable filehandle so the
* parent can be resolved at decode time. Derive this from the
* delegation's export rather than the shared nfs4_file, which may
* have been initialized under a different export.
*/
if (exp && !(exp->ex_flags & NFSEXP_NOSUBTREECHECK) &&
!S_ISDIR(inode->i_mode)) {
parent = d_inode(nf->nf_file->f_path.dentry);
flags = EXPORT_FH_CONNECTABLE;
}
fileid_type = exportfs_encode_inode_fh(inode, fid, &maxsize, parent, flags);
if (fileid_type < 0 || fileid_type == FILEID_INVALID)
goto out;
fhp->fh_fileid_type = fileid_type;
fhp->fh_size += maxsize * 4;
if (exp && (exp->ex_flags & NFSEXP_SIGN_FH))
if (!fh_append_mac(fhp, NFS4_FHSIZE, exp->cd->net))
goto out;
ret = true;
out:
if (exp)
exp_put(exp);
return ret;
}
#define CB_NOTIFY_STATX_REQUEST_MASK (STATX_BASIC_STATS | \
STATX_BTIME | \
STATX_CHANGE_COOKIE)
static bool
nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
struct dentry *dentry, struct nfs4_delegation *dp,
struct nfsd_file *nf, char *name, u32 namelen)
{
struct path path = nf->nf_file->f_path;
struct nfsd4_fattr_args args = { };
const u32 *reqmask;
uint32_t *attrmask;
__be32 status;
bool parent;
int ret;
/* Reserve space for attrmask */
attrmask = xdr_reserve_space(xdr, 3 * sizeof(uint32_t));
if (!attrmask)
return false;
ne->ne_file.data = name;
ne->ne_file.len = namelen;
ne->ne_attrs.attrmask.element = attrmask;
parent = (dentry == path.dentry);
path.dentry = dentry;
reqmask = parent ? dp->dl_dir_attrs : dp->dl_child_attrs;
/*
* A NULL or negative dentry has no attributes to report (expected,
* e.g. for the old entry of a rename or an entry already removed).
* The client may also have been granted the notification while
* requesting no attributes for this entry. Both cases encode an
* empty attribute set rather than failing: the vfs_getattr() and
* nfsd4_encode_attr_vals() failures below recall the delegation, so
* a case with nothing to fetch must short-circuit ahead of them.
*/
if (!path.dentry || !d_inode(path.dentry) ||
(!reqmask[0] && !reqmask[1])) {
attrmask[0] = 0;
attrmask[1] = 0;
attrmask[2] = 0;
ne->ne_attrs.attr_vals.data = NULL;
ne->ne_attrs.attr_vals.len = 0;
ne->ne_attrs.attrmask.count = 1;
return true;
}
/*
* It is possible that the client was granted a delegation when a file
* was created. Note that we don't issue a CB_GETATTR here since stale
* attributes are presumably ok.
*/
ret = vfs_getattr(&path, &args.stat, CB_NOTIFY_STATX_REQUEST_MASK, AT_STATX_SYNC_AS_STAT);
if (ret)
return false;
args.change_attr = nfsd4_change_attribute(&args.stat);
if (parent) {
attrmask[0] = dp->dl_dir_attrs[0];
attrmask[1] = dp->dl_dir_attrs[1];
} else {
attrmask[0] = dp->dl_child_attrs[0];
attrmask[1] = dp->dl_child_attrs[1];
if (!setup_notify_fhandle(dentry, dp, nf, &args))
attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
if (!(args.stat.result_mask & STATX_BTIME))
attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
}
attrmask[2] = 0;
ne->ne_attrs.attrmask.count = 2;
ne->ne_attrs.attr_vals.data = (u8 *)xdr->p;
status = nfsd4_encode_attr_vals(xdr, attrmask, &args);
if (status != nfs_ok)
return false;
ne->ne_attrs.attr_vals.len = (u8 *)xdr->p - ne->ne_attrs.attr_vals.data;
return true;
}
/**
* nfsd4_encode_notify_event - encode a notify
* @xdr: stream to which to encode the fattr4
* @nne: nfsd_notify_event to encode
* @dp: delegation where the event occurred
* @nf: nfsd_file on which event occurred
* @notify_mask: pointer to word where notification mask should be set
*
* Encode @nne into @xdr. The matching bit in @notify_mask is set on
* success.
*
* Return: pointer to the start of the encoded event, or NULL if the
* event could not be encoded.
*/
u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *nne,
struct nfs4_delegation *dp, struct nfsd_file *nf,
u32 *notify_mask)
{
u8 *p = NULL;
*notify_mask = 0;
if (nne->ne_mask & FS_DELETE) {
struct notify_remove4 nr = { };
if (!nfsd4_setup_notify_entry4(&nr.nrm_old_entry, xdr, nne->ne_dentry, dp,
nf, nne->ne_name, nne->ne_namelen))
goto out_err;
p = (u8 *)xdr->p;
if (!xdrgen_encode_notify_remove4(xdr, &nr))
goto out_err;
*notify_mask |= BIT(NOTIFY4_REMOVE_ENTRY);
} else if (nne->ne_mask & FS_CREATE) {
struct notify_add4 na = { };
struct notify_remove4 old = { };
if (!nfsd4_setup_notify_entry4(&na.nad_new_entry, xdr, nne->ne_dentry, dp,
nf, nne->ne_name, nne->ne_namelen))
goto out_err;
/* If a file was overwritten, report it in nad_old_entry */
if (nne->ne_target) {
if (!nfsd4_setup_notify_entry4(&old.nrm_old_entry, xdr,
NULL, dp, nf,
nne->ne_name, nne->ne_namelen))
goto out_err;
na.nad_old_entry.count = 1;
na.nad_old_entry.element = &old;
}
p = (u8 *)xdr->p;
if (!xdrgen_encode_notify_add4(xdr, &na))
goto out_err;
*notify_mask |= BIT(NOTIFY4_ADD_ENTRY);
} else if (nne->ne_mask & FS_RENAME) {
struct notify_rename4 nr = { };
struct notify_remove4 old = { };
char *newname = nfsd_notify_event_newname(nne);
/* Don't send any attributes in the old_entry since they're the same in new */
if (!nfsd4_setup_notify_entry4(&nr.nrn_old_entry.nrm_old_entry, xdr,
NULL, dp, nf, nne->ne_name,
nne->ne_namelen))
goto out_err;
if (!nfsd4_setup_notify_entry4(&nr.nrn_new_entry.nad_new_entry, xdr,
nne->ne_dentry, dp, nf, newname,
nne->ne_newnamelen))
goto out_err;
/* If a file was overwritten, report it in nad_old_entry */
if (nne->ne_target) {
if (!nfsd4_setup_notify_entry4(&old.nrm_old_entry, xdr,
NULL, dp, nf, newname,
nne->ne_newnamelen))
goto out_err;
nr.nrn_new_entry.nad_old_entry.count = 1;
nr.nrn_new_entry.nad_old_entry.element = &old;
}
p = (u8 *)xdr->p;
if (!xdrgen_encode_notify_rename4(xdr, &nr))
goto out_err;
*notify_mask |= BIT(NOTIFY4_RENAME_ENTRY);
}
return p;
out_err:
pr_warn("nfsd: unable to marshal notify event to xdr stream\n");
return NULL;
}
/**
* nfsd4_encode_dir_attr_change
* @xdr: stream to which to encode the fattr4
* @dp: delegation where the event occurred
* @nf: nfsd_file opened on the directory
*
* Encode a dir attr change event.
*
* Return: a pointer to the start of the encoded event on success; NULL
* if there were no requested attributes to report, in which case the
* caller should omit the event; or an ERR_PTR if the event was requested
* but could not be marshalled into @xdr, in which case the caller should
* recall the delegation.
*/
u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct nfs4_delegation *dp,
struct nfsd_file *nf)
{
struct dentry *dentry = nf->nf_file->f_path.dentry;
struct notify_attr4 na = { };
u8 *p;
/* RFC 8881 s10.4.3: ne_file must be a zero-length string for dir attrs */
if (!nfsd4_setup_notify_entry4(&na.na_changed_entry, xdr,
dentry, dp, nf, "", 0))
return ERR_PTR(-ENOBUFS);
/* No requested attributes to report; omit the event */
if (!na.na_changed_entry.ne_attrs.attr_vals.len)
return NULL;
p = (u8 *)xdr->p;
if (!xdrgen_encode_notify_attr4(xdr, &na))
return ERR_PTR(-ENOBUFS);
return p;
}
static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
@@ -4323,7 +4644,7 @@ nfsd4_encode_entry4(void *ccdv, const char *name, int namlen,
__be32 nfserr = nfserr_toosmall;
/* In nfsv4, "." and ".." never make it onto the wire.. */
if (name && isdotent(name, namlen)) {
if (name && name_is_dot_dotdot(name, namlen)) {
cd->common.err = nfs_ok;
return 0;
}
@@ -6390,9 +6711,6 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
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);
/*
* Account for pages consumed while encoding this operation.
* The xdr_stream primitives don't manage rq_next_page.
@@ -6424,9 +6742,12 @@ void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
{
struct nfsd4_compoundargs *args = rqstp->rq_argp;
args->opcnt = 0;
if (args->ops != args->iops) {
vfree(args->ops);
void *old_ops = args->ops;
args->ops = args->iops;
kvfree_rcu_mightsleep(old_ops);
}
while (args->to_free) {
struct svcxdr_tmpbuf *tb = args->to_free;

View File

@@ -1,12 +1,24 @@
// 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: Thu Jan 8 23:12:07 2026
// XDR specification modification time: Tue Jun 30 11:57:21 2026
#include <linux/sunrpc/svc.h>
#include "nfs4xdr_gen.h"
static bool __maybe_unused
xdrgen_decode_int32_t(struct xdr_stream *xdr, int32_t *ptr)
{
return xdrgen_decode_int(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_int64_t(struct xdr_stream *xdr, int64_t *ptr)
{
@@ -14,9 +26,145 @@ xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
}
static bool __maybe_unused
xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
xdrgen_decode_uint64_t(struct xdr_stream *xdr, uint64_t *ptr)
{
return xdrgen_decode_unsigned_int(xdr, ptr);
return xdrgen_decode_unsigned_hyper(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_nfsstat4(struct xdr_stream *xdr, nfsstat4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case NFS4_OK:
case NFS4ERR_PERM:
case NFS4ERR_NOENT:
case NFS4ERR_IO:
case NFS4ERR_NXIO:
case NFS4ERR_ACCESS:
case NFS4ERR_EXIST:
case NFS4ERR_XDEV:
case NFS4ERR_NOTDIR:
case NFS4ERR_ISDIR:
case NFS4ERR_INVAL:
case NFS4ERR_FBIG:
case NFS4ERR_NOSPC:
case NFS4ERR_ROFS:
case NFS4ERR_MLINK:
case NFS4ERR_NAMETOOLONG:
case NFS4ERR_NOTEMPTY:
case NFS4ERR_DQUOT:
case NFS4ERR_STALE:
case NFS4ERR_BADHANDLE:
case NFS4ERR_BAD_COOKIE:
case NFS4ERR_NOTSUPP:
case NFS4ERR_TOOSMALL:
case NFS4ERR_SERVERFAULT:
case NFS4ERR_BADTYPE:
case NFS4ERR_DELAY:
case NFS4ERR_SAME:
case NFS4ERR_DENIED:
case NFS4ERR_EXPIRED:
case NFS4ERR_LOCKED:
case NFS4ERR_GRACE:
case NFS4ERR_FHEXPIRED:
case NFS4ERR_SHARE_DENIED:
case NFS4ERR_WRONGSEC:
case NFS4ERR_CLID_INUSE:
case NFS4ERR_RESOURCE:
case NFS4ERR_MOVED:
case NFS4ERR_NOFILEHANDLE:
case NFS4ERR_MINOR_VERS_MISMATCH:
case NFS4ERR_STALE_CLIENTID:
case NFS4ERR_STALE_STATEID:
case NFS4ERR_OLD_STATEID:
case NFS4ERR_BAD_STATEID:
case NFS4ERR_BAD_SEQID:
case NFS4ERR_NOT_SAME:
case NFS4ERR_LOCK_RANGE:
case NFS4ERR_SYMLINK:
case NFS4ERR_RESTOREFH:
case NFS4ERR_LEASE_MOVED:
case NFS4ERR_ATTRNOTSUPP:
case NFS4ERR_NO_GRACE:
case NFS4ERR_RECLAIM_BAD:
case NFS4ERR_RECLAIM_CONFLICT:
case NFS4ERR_BADXDR:
case NFS4ERR_LOCKS_HELD:
case NFS4ERR_OPENMODE:
case NFS4ERR_BADOWNER:
case NFS4ERR_BADCHAR:
case NFS4ERR_BADNAME:
case NFS4ERR_BAD_RANGE:
case NFS4ERR_LOCK_NOTSUPP:
case NFS4ERR_OP_ILLEGAL:
case NFS4ERR_DEADLOCK:
case NFS4ERR_FILE_OPEN:
case NFS4ERR_ADMIN_REVOKED:
case NFS4ERR_CB_PATH_DOWN:
case NFS4ERR_BADIOMODE:
case NFS4ERR_BADLAYOUT:
case NFS4ERR_BAD_SESSION_DIGEST:
case NFS4ERR_BADSESSION:
case NFS4ERR_BADSLOT:
case NFS4ERR_COMPLETE_ALREADY:
case NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
case NFS4ERR_DELEG_ALREADY_WANTED:
case NFS4ERR_BACK_CHAN_BUSY:
case NFS4ERR_LAYOUTTRYLATER:
case NFS4ERR_LAYOUTUNAVAILABLE:
case NFS4ERR_NOMATCHING_LAYOUT:
case NFS4ERR_RECALLCONFLICT:
case NFS4ERR_UNKNOWN_LAYOUTTYPE:
case NFS4ERR_SEQ_MISORDERED:
case NFS4ERR_SEQUENCE_POS:
case NFS4ERR_REQ_TOO_BIG:
case NFS4ERR_REP_TOO_BIG:
case NFS4ERR_REP_TOO_BIG_TO_CACHE:
case NFS4ERR_RETRY_UNCACHED_REP:
case NFS4ERR_UNSAFE_COMPOUND:
case NFS4ERR_TOO_MANY_OPS:
case NFS4ERR_OP_NOT_IN_SESSION:
case NFS4ERR_HASH_ALG_UNSUPP:
case NFS4ERR_CLIENTID_BUSY:
case NFS4ERR_PNFS_IO_HOLE:
case NFS4ERR_SEQ_FALSE_RETRY:
case NFS4ERR_BAD_HIGH_SLOT:
case NFS4ERR_DEADSESSION:
case NFS4ERR_ENCR_ALG_UNSUPP:
case NFS4ERR_PNFS_NO_LAYOUT:
case NFS4ERR_NOT_ONLY_OP:
case NFS4ERR_WRONG_CRED:
case NFS4ERR_WRONG_TYPE:
case NFS4ERR_DIRDELEG_UNAVAIL:
case NFS4ERR_REJECT_DELEG:
case NFS4ERR_RETURNCONFLICT:
case NFS4ERR_DELEG_REVOKED:
case NFS4ERR_PARTNER_NOTSUPP:
case NFS4ERR_PARTNER_NO_AUTH:
case NFS4ERR_UNION_NOTSUPP:
case NFS4ERR_OFFLOAD_DENIED:
case NFS4ERR_WRONG_LFS:
case NFS4ERR_BADLABEL:
case NFS4ERR_OFFLOAD_NO_REQS:
case NFS4ERR_NOXATTR:
case NFS4ERR_XATTR2BIG:
break;
default:
return false;
}
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_attrlist4(struct xdr_stream *xdr, attrlist4 *ptr)
{
return xdrgen_decode_opaque(xdr, ptr, 0);
}
static bool __maybe_unused
@@ -30,6 +178,24 @@ xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
return true;
}
static bool __maybe_unused
xdrgen_decode_verifier4(struct xdr_stream *xdr, verifier4 *ptr)
{
return xdr_stream_decode_opaque_fixed(xdr, ptr, NFS4_VERIFIER_SIZE) == 0;
}
static bool __maybe_unused
xdrgen_decode_nfs_cookie4(struct xdr_stream *xdr, nfs_cookie4 *ptr)
{
return xdrgen_decode_uint64_t(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_nfs_fh4(struct xdr_stream *xdr, nfs_fh4 *ptr)
{
return xdrgen_decode_opaque(xdr, ptr, NFS4_FHSIZE);
}
static bool __maybe_unused
xdrgen_decode_utf8string(struct xdr_stream *xdr, utf8string *ptr)
{
@@ -54,6 +220,29 @@ xdrgen_decode_utf8str_mixed(struct xdr_stream *xdr, utf8str_mixed *ptr)
return xdrgen_decode_utf8string(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_component4(struct xdr_stream *xdr, component4 *ptr)
{
return xdrgen_decode_utf8str_cs(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_linktext4(struct xdr_stream *xdr, linktext4 *ptr)
{
return xdrgen_decode_utf8str_cs(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_pathname4(struct xdr_stream *xdr, pathname4 *ptr)
{
if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
return false;
for (u32 i = 0; i < ptr->count; i++)
if (!xdrgen_decode_component4(xdr, &ptr->element[i]))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
{
@@ -64,6 +253,26 @@ xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
return true;
}
static bool __maybe_unused
xdrgen_decode_fattr4(struct xdr_stream *xdr, struct fattr4 *ptr)
{
if (!xdrgen_decode_bitmap4(xdr, &ptr->attrmask))
return false;
if (!xdrgen_decode_attrlist4(xdr, &ptr->attr_vals))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_stateid4(struct xdr_stream *xdr, struct stateid4 *ptr)
{
if (!xdrgen_decode_uint32_t(xdr, &ptr->seqid))
return false;
if (xdr_stream_decode_opaque_fixed(xdr, ptr->other, 12) < 0)
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
{
@@ -366,9 +575,171 @@ xdrgen_decode_fattr4_posix_access_acl(struct xdr_stream *xdr, fattr4_posix_acces
*/
static bool __maybe_unused
xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
xdrgen_decode_notify_type4(struct xdr_stream *xdr, notify_type4 *ptr)
{
return xdrgen_encode_hyper(xdr, value);
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case NOTIFY4_CHANGE_CHILD_ATTRS:
case NOTIFY4_CHANGE_DIR_ATTRS:
case NOTIFY4_REMOVE_ENTRY:
case NOTIFY4_ADD_ENTRY:
case NOTIFY4_RENAME_ENTRY:
case NOTIFY4_CHANGE_COOKIE_VERIFIER:
case NOTIFY4_GFLAG_EXTEND:
case NOTIFY4_AUFLAG_VALID:
case NOTIFY4_AUFLAG_USER:
case NOTIFY4_AUFLAG_GROUP:
case NOTIFY4_AUFLAG_OTHER:
case NOTIFY4_CHANGE_AUTH:
case NOTIFY4_CFLAG_ORDER:
case NOTIFY4_AUFLAG_GANOW:
case NOTIFY4_AUFLAG_GALATER:
case NOTIFY4_CHANGE_GA:
case NOTIFY4_CHANGE_AMASK:
break;
default:
return false;
}
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_notify_entry4(struct xdr_stream *xdr, struct notify_entry4 *ptr)
{
if (!xdrgen_decode_component4(xdr, &ptr->ne_file))
return false;
if (!xdrgen_decode_fattr4(xdr, &ptr->ne_attrs))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_prev_entry4(struct xdr_stream *xdr, struct prev_entry4 *ptr)
{
if (!xdrgen_decode_notify_entry4(xdr, &ptr->pe_prev_entry))
return false;
if (!xdrgen_decode_nfs_cookie4(xdr, &ptr->pe_prev_entry_cookie))
return false;
return true;
}
bool
xdrgen_decode_notify_remove4(struct xdr_stream *xdr, struct notify_remove4 *ptr)
{
if (!xdrgen_decode_notify_entry4(xdr, &ptr->nrm_old_entry))
return false;
if (!xdrgen_decode_nfs_cookie4(xdr, &ptr->nrm_old_entry_cookie))
return false;
return true;
}
bool
xdrgen_decode_notify_add4(struct xdr_stream *xdr, struct notify_add4 *ptr)
{
if (xdr_stream_decode_u32(xdr, &ptr->nad_old_entry.count) < 0)
return false;
if (ptr->nad_old_entry.count > 1)
return false;
for (u32 i = 0; i < ptr->nad_old_entry.count; i++)
if (!xdrgen_decode_notify_remove4(xdr, &ptr->nad_old_entry.element[i]))
return false;
if (!xdrgen_decode_notify_entry4(xdr, &ptr->nad_new_entry))
return false;
if (xdr_stream_decode_u32(xdr, &ptr->nad_new_entry_cookie.count) < 0)
return false;
if (ptr->nad_new_entry_cookie.count > 1)
return false;
for (u32 i = 0; i < ptr->nad_new_entry_cookie.count; i++)
if (!xdrgen_decode_nfs_cookie4(xdr, &ptr->nad_new_entry_cookie.element[i]))
return false;
if (xdr_stream_decode_u32(xdr, &ptr->nad_prev_entry.count) < 0)
return false;
if (ptr->nad_prev_entry.count > 1)
return false;
for (u32 i = 0; i < ptr->nad_prev_entry.count; i++)
if (!xdrgen_decode_prev_entry4(xdr, &ptr->nad_prev_entry.element[i]))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->nad_last_entry))
return false;
return true;
}
bool
xdrgen_decode_notify_attr4(struct xdr_stream *xdr, struct notify_attr4 *ptr)
{
if (!xdrgen_decode_notify_entry4(xdr, &ptr->na_changed_entry))
return false;
return true;
}
bool
xdrgen_decode_notify_rename4(struct xdr_stream *xdr, struct notify_rename4 *ptr)
{
if (!xdrgen_decode_notify_remove4(xdr, &ptr->nrn_old_entry))
return false;
if (!xdrgen_decode_notify_add4(xdr, &ptr->nrn_new_entry))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_notify_verifier4(struct xdr_stream *xdr, struct notify_verifier4 *ptr)
{
if (!xdrgen_decode_verifier4(xdr, &ptr->nv_old_cookieverf))
return false;
if (!xdrgen_decode_verifier4(xdr, &ptr->nv_new_cookieverf))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_notifylist4(struct xdr_stream *xdr, notifylist4 *ptr)
{
return xdrgen_decode_opaque(xdr, ptr, 0);
}
static bool __maybe_unused
xdrgen_decode_notify4(struct xdr_stream *xdr, struct notify4 *ptr)
{
if (!xdrgen_decode_bitmap4(xdr, &ptr->notify_mask))
return false;
if (!xdrgen_decode_notifylist4(xdr, &ptr->notify_vals))
return false;
return true;
}
bool
xdrgen_decode_CB_NOTIFY4args(struct xdr_stream *xdr, struct CB_NOTIFY4args *ptr)
{
if (!xdrgen_decode_stateid4(xdr, &ptr->cna_stateid))
return false;
if (!xdrgen_decode_nfs_fh4(xdr, &ptr->cna_fh))
return false;
if (xdr_stream_decode_u32(xdr, &ptr->cna_changes.count) < 0)
return false;
for (u32 i = 0; i < ptr->cna_changes.count; i++)
if (!xdrgen_decode_notify4(xdr, &ptr->cna_changes.element[i]))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_CB_NOTIFY4res(struct xdr_stream *xdr, struct CB_NOTIFY4res *ptr)
{
if (!xdrgen_decode_nfsstat4(xdr, &ptr->cnr_status))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_int32_t(struct xdr_stream *xdr, const int32_t value)
{
return xdrgen_encode_int(xdr, value);
}
static bool __maybe_unused
@@ -377,6 +748,30 @@ 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_int64_t(struct xdr_stream *xdr, const int64_t value)
{
return xdrgen_encode_hyper(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_uint64_t(struct xdr_stream *xdr, const uint64_t value)
{
return xdrgen_encode_unsigned_hyper(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_nfsstat4(struct xdr_stream *xdr, nfsstat4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_attrlist4(struct xdr_stream *xdr, const attrlist4 value)
{
return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
}
static bool __maybe_unused
xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
{
@@ -388,6 +783,24 @@ xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
return true;
}
static bool __maybe_unused
xdrgen_encode_verifier4(struct xdr_stream *xdr, const verifier4 value)
{
return xdr_stream_encode_opaque_fixed(xdr, value, NFS4_VERIFIER_SIZE) >= 0;
}
static bool __maybe_unused
xdrgen_encode_nfs_cookie4(struct xdr_stream *xdr, const nfs_cookie4 value)
{
return xdrgen_encode_uint64_t(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_nfs_fh4(struct xdr_stream *xdr, const nfs_fh4 value)
{
return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
}
static bool __maybe_unused
xdrgen_encode_utf8string(struct xdr_stream *xdr, const utf8string value)
{
@@ -412,6 +825,29 @@ xdrgen_encode_utf8str_mixed(struct xdr_stream *xdr, const utf8str_mixed value)
return xdrgen_encode_utf8string(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_component4(struct xdr_stream *xdr, const component4 value)
{
return xdrgen_encode_utf8str_cs(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_linktext4(struct xdr_stream *xdr, const linktext4 value)
{
return xdrgen_encode_utf8str_cs(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_pathname4(struct xdr_stream *xdr, const pathname4 value)
{
if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value.count; i++)
if (!xdrgen_encode_component4(xdr, value.element[i]))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
{
@@ -422,6 +858,26 @@ xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
return true;
}
static bool __maybe_unused
xdrgen_encode_fattr4(struct xdr_stream *xdr, const struct fattr4 *value)
{
if (!xdrgen_encode_bitmap4(xdr, value->attrmask))
return false;
if (!xdrgen_encode_attrlist4(xdr, value->attr_vals))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_stateid4(struct xdr_stream *xdr, const struct stateid4 *value)
{
if (!xdrgen_encode_uint32_t(xdr, value->seqid))
return false;
if (xdr_stream_encode_opaque_fixed(xdr, value->other, 12) < 0)
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
{
@@ -567,3 +1023,137 @@ xdrgen_encode_fattr4_posix_access_acl(struct xdr_stream *xdr, const fattr4_posix
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_notify_type4(struct xdr_stream *xdr, notify_type4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_notify_entry4(struct xdr_stream *xdr, const struct notify_entry4 *value)
{
if (!xdrgen_encode_component4(xdr, value->ne_file))
return false;
if (!xdrgen_encode_fattr4(xdr, &value->ne_attrs))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_prev_entry4(struct xdr_stream *xdr, const struct prev_entry4 *value)
{
if (!xdrgen_encode_notify_entry4(xdr, &value->pe_prev_entry))
return false;
if (!xdrgen_encode_nfs_cookie4(xdr, value->pe_prev_entry_cookie))
return false;
return true;
}
bool
xdrgen_encode_notify_remove4(struct xdr_stream *xdr, const struct notify_remove4 *value)
{
if (!xdrgen_encode_notify_entry4(xdr, &value->nrm_old_entry))
return false;
if (!xdrgen_encode_nfs_cookie4(xdr, value->nrm_old_entry_cookie))
return false;
return true;
}
bool
xdrgen_encode_notify_add4(struct xdr_stream *xdr, const struct notify_add4 *value)
{
if (value->nad_old_entry.count > 1)
return false;
if (xdr_stream_encode_u32(xdr, value->nad_old_entry.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value->nad_old_entry.count; i++)
if (!xdrgen_encode_notify_remove4(xdr, &value->nad_old_entry.element[i]))
return false;
if (!xdrgen_encode_notify_entry4(xdr, &value->nad_new_entry))
return false;
if (value->nad_new_entry_cookie.count > 1)
return false;
if (xdr_stream_encode_u32(xdr, value->nad_new_entry_cookie.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value->nad_new_entry_cookie.count; i++)
if (!xdrgen_encode_nfs_cookie4(xdr, value->nad_new_entry_cookie.element[i]))
return false;
if (value->nad_prev_entry.count > 1)
return false;
if (xdr_stream_encode_u32(xdr, value->nad_prev_entry.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value->nad_prev_entry.count; i++)
if (!xdrgen_encode_prev_entry4(xdr, &value->nad_prev_entry.element[i]))
return false;
if (!xdrgen_encode_bool(xdr, value->nad_last_entry))
return false;
return true;
}
bool
xdrgen_encode_notify_attr4(struct xdr_stream *xdr, const struct notify_attr4 *value)
{
if (!xdrgen_encode_notify_entry4(xdr, &value->na_changed_entry))
return false;
return true;
}
bool
xdrgen_encode_notify_rename4(struct xdr_stream *xdr, const struct notify_rename4 *value)
{
if (!xdrgen_encode_notify_remove4(xdr, &value->nrn_old_entry))
return false;
if (!xdrgen_encode_notify_add4(xdr, &value->nrn_new_entry))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_notify_verifier4(struct xdr_stream *xdr, const struct notify_verifier4 *value)
{
if (!xdrgen_encode_verifier4(xdr, value->nv_old_cookieverf))
return false;
if (!xdrgen_encode_verifier4(xdr, value->nv_new_cookieverf))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_notifylist4(struct xdr_stream *xdr, const notifylist4 value)
{
return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
}
static bool __maybe_unused
xdrgen_encode_notify4(struct xdr_stream *xdr, const struct notify4 *value)
{
if (!xdrgen_encode_bitmap4(xdr, value->notify_mask))
return false;
if (!xdrgen_encode_notifylist4(xdr, value->notify_vals))
return false;
return true;
}
bool
xdrgen_encode_CB_NOTIFY4args(struct xdr_stream *xdr, const struct CB_NOTIFY4args *value)
{
if (!xdrgen_encode_stateid4(xdr, &value->cna_stateid))
return false;
if (!xdrgen_encode_nfs_fh4(xdr, value->cna_fh))
return false;
if (xdr_stream_encode_u32(xdr, value->cna_changes.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value->cna_changes.count; i++)
if (!xdrgen_encode_notify4(xdr, &value->cna_changes.element[i]))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_CB_NOTIFY4res(struct xdr_stream *xdr, const struct CB_NOTIFY4res *value)
{
if (!xdrgen_encode_nfsstat4(xdr, value->cnr_status))
return false;
return true;
}

View File

@@ -1,7 +1,7 @@
/* 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: Thu Jan 8 23:12:07 2026 */
/* XDR specification modification time: Tue Jun 30 11:57:21 2026 */
#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
@@ -21,10 +21,13 @@ bool xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4
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);
bool xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr);
bool xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value);
bool xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr);
bool xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value);
bool xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr);
bool xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value);
@@ -32,4 +35,19 @@ bool xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr);
bool xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value);
bool xdrgen_decode_notify_remove4(struct xdr_stream *xdr, struct notify_remove4 *ptr);
bool xdrgen_encode_notify_remove4(struct xdr_stream *xdr, const struct notify_remove4 *value);
bool xdrgen_decode_notify_add4(struct xdr_stream *xdr, struct notify_add4 *ptr);
bool xdrgen_encode_notify_add4(struct xdr_stream *xdr, const struct notify_add4 *value);
bool xdrgen_decode_notify_attr4(struct xdr_stream *xdr, struct notify_attr4 *ptr);
bool xdrgen_encode_notify_attr4(struct xdr_stream *xdr, const struct notify_attr4 *value);
bool xdrgen_decode_notify_rename4(struct xdr_stream *xdr, struct notify_rename4 *ptr);
bool xdrgen_encode_notify_rename4(struct xdr_stream *xdr, const struct notify_rename4 *value);
bool xdrgen_decode_CB_NOTIFY4args(struct xdr_stream *xdr, struct CB_NOTIFY4args *ptr);
bool xdrgen_encode_CB_NOTIFY4args(struct xdr_stream *xdr, const struct CB_NOTIFY4args *value);
#endif /* _LINUX_XDRGEN_NFS4_1_DECL_H */

View File

@@ -19,6 +19,8 @@
#include <net/checksum.h>
#include "nfsd.h"
#include "netns.h"
#include "stats.h"
#include "cache.h"
#include "trace.h"
@@ -200,14 +202,14 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
nn->nfsd_reply_cache_shrinker->seeks = 1;
nn->nfsd_reply_cache_shrinker->private_data = nn;
shrinker_register(nn->nfsd_reply_cache_shrinker);
for (i = 0; i < hashsize; i++) {
INIT_LIST_HEAD(&nn->drc_hashtbl[i].lru_head);
spin_lock_init(&nn->drc_hashtbl[i].cache_lock);
}
nn->drc_hashsize = hashsize;
shrinker_register(nn->nfsd_reply_cache_shrinker);
return 0;
out_shrinker:
kvfree(nn->drc_hashtbl);
@@ -275,7 +277,7 @@ nfsd_prune_bucket_locked(struct nfsd_net *nn, struct nfsd_drc_bucket *b,
nfsd_cacherep_unlink_locked(nn, b, rp);
list_add(&rp->c_lru, dispose);
if (max && ++freed > max)
if (max && ++freed >= max)
break;
}
}

View File

@@ -23,6 +23,8 @@
#include "idmap.h"
#include "nfsd.h"
#include "netns.h"
#include "stats.h"
#include "cache.h"
#include "state.h"
#include "netns.h"
@@ -296,14 +298,15 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
* 2. Is that directory a mount point, or
* 3. Is that directory the root of an exported file system?
*/
nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb);
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
mutex_lock(&nfsd_mutex);
nn = net_generic(netns(file), nfsd_net_id);
if (nn->nfsd_serv)
if (test_bit(NFSD_NET_UP, &nn->flags)) {
nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb);
nfsd4_revoke_states(nn, path.dentry->d_sb);
else
} else {
error = -EINVAL;
}
mutex_unlock(&nfsd_mutex);
path_put(&path);
@@ -420,6 +423,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
char *mesg = buf;
int rv;
struct net *net = netns(file);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (size > 0) {
int newthreads;
@@ -430,7 +434,10 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return -EINVAL;
trace_nfsd_ctl_threads(net, newthreads);
mutex_lock(&nfsd_mutex);
rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
if (newthreads > 0 || nn->nfsd_serv != NULL)
rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
else
rv = 0;
mutex_unlock(&nfsd_mutex);
if (rv < 0)
return rv;
@@ -1111,7 +1118,7 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
}
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n",
nn->grace_ended ? 'Y' : 'N');
test_bit(NFSD_NET_GRACE_ENDED, &nn->flags) ? 'Y' : 'N');
}
#endif
@@ -1414,8 +1421,8 @@ static int create_proc_exports_entry(void)
unsigned int nfsd_net_id;
struct nfsd_genl_rqstp {
struct sockaddr rq_daddr;
struct sockaddr rq_saddr;
struct sockaddr_storage rq_daddr;
struct sockaddr_storage rq_saddr;
unsigned long rq_flags;
ktime_t rq_stime;
__be32 rq_xid;
@@ -1448,9 +1455,9 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME,
ktime_to_us(genl_rqstp->rq_stime),
NFSD_A_RPC_STATUS_PAD))
return -ENOBUFS;
goto out_cancel;
switch (genl_rqstp->rq_saddr.sa_family) {
switch (genl_rqstp->rq_saddr.ss_family) {
case AF_INET: {
const struct sockaddr_in *s_in, *d_in;
@@ -1464,7 +1471,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
s_in->sin_port) ||
nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT,
d_in->sin_port))
return -ENOBUFS;
goto out_cancel;
break;
}
case AF_INET6: {
@@ -1480,7 +1487,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
s_in->sin6_port) ||
nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT,
d_in->sin6_port))
return -ENOBUFS;
goto out_cancel;
break;
}
}
@@ -1488,10 +1495,14 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
for (i = 0; i < genl_rqstp->rq_opcnt; i++)
if (nla_put_u32(skb, NFSD_A_RPC_STATUS_COMPOUND_OPS,
genl_rqstp->rq_opnum[i]))
return -ENOBUFS;
goto out_cancel;
genlmsg_end(skb, hdr);
return 0;
out_cancel:
genlmsg_cancel(skb, hdr);
return -ENOBUFS;
}
/**
@@ -1517,20 +1528,30 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
rcu_read_lock();
for (i = 0; i < nn->nfsd_serv->sv_nrpools; i++) {
for (i = 0; i < svc_serv_nrpools(nn->nfsd_serv); i++) {
struct svc_rqst *rqstp;
long thread_skip = 0;
if (i < cb->args[0]) /* already consumed */
continue;
/*
* The saved thread index only applies to the pool the dump
* was resumed in. Subsequent pools must start from thread 0,
* otherwise their first cb->args[1] threads are silently
* skipped.
*/
if (i == cb->args[0])
thread_skip = cb->args[1];
rqstp_index = 0;
list_for_each_entry_rcu(rqstp,
&nn->nfsd_serv->sv_pools[i].sp_all_threads,
rq_all) {
struct nfsd_genl_rqstp genl_rqstp;
struct nfsd_genl_rqstp genl_rqstp = {};
unsigned int status_counter;
if (rqstp_index++ < cb->args[1]) /* already consumed */
if (rqstp_index++ < thread_skip) /* already consumed */
continue;
/*
* Acquire rq_status_counter before parsing the rqst
@@ -1551,9 +1572,9 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
genl_rqstp.rq_stime = rqstp->rq_stime;
genl_rqstp.rq_opcnt = 0;
memcpy(&genl_rqstp.rq_daddr, svc_daddr(rqstp),
sizeof(struct sockaddr));
sizeof(struct sockaddr_storage));
memcpy(&genl_rqstp.rq_saddr, svc_addr(rqstp),
sizeof(struct sockaddr));
sizeof(struct sockaddr_storage));
#ifdef CONFIG_NFSD_V4
if (rqstp->rq_vers == NFS4_VERSION &&
@@ -1572,17 +1593,26 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
#endif /* CONFIG_NFSD_V4 */
/*
* Acquire rq_status_counter before reporting the rqst
* fields to the user.
* Read-side load-load fence: order the field reads
* above before the counter re-read below, mirroring
* the smp_rmb() in the standard seqcount retry. The
* begin-side smp_load_acquire() above pairs with the
* smp_store_release() in nfsd_dispatch().
*/
if (smp_load_acquire(&rqstp->rq_status_counter) !=
status_counter)
smp_rmb();
if (READ_ONCE(rqstp->rq_status_counter) != status_counter)
continue;
ret = nfsd_genl_rpc_status_compose_msg(skb, cb,
&genl_rqstp);
if (ret)
if (ret) {
if (skb->len) {
cb->args[0] = i;
cb->args[1] = rqstp_index - 1;
ret = skb->len;
}
goto out;
}
}
}
@@ -1943,6 +1973,60 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
return err;
}
/**
* nfsd_nl_validate_listeners - sanity-check the listener list from userland
* @info: netlink metadata and command arguments
*
* Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that each entry
* is well-formed: it parses against the policy, carries both an address and
* a transport name, and the address is long enough for its family. Doing
* this up front lets the callers below assume every entry is valid and
* guarantees we make no changes when the request is malformed.
*
* Return: 0 if every entry is valid, or a negative errno otherwise.
*/
static int nfsd_nl_validate_listeners(struct genl_info *info)
{
const struct nlattr *attr;
int rem;
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
GENL_HDRLEN, rem) {
struct nlattr *tb[NFSD_A_SOCK_MAX + 1];
struct sockaddr *sa;
int err;
err = nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack);
if (err < 0)
return err;
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
return -EINVAL;
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(sa->sa_family))
return -EINVAL;
switch (sa->sa_family) {
case AF_INET:
if (nla_len(tb[NFSD_A_SOCK_ADDR]) <
sizeof(struct sockaddr_in))
return -EINVAL;
break;
case AF_INET6:
if (nla_len(tb[NFSD_A_SOCK_ADDR]) <
sizeof(struct sockaddr_in6))
return -EINVAL;
break;
default:
return -EAFNOSUPPORT;
}
}
return 0;
}
/**
* nfsd_nl_listener_set_doit - set the nfs running sockets
* @skb: reply buffer
@@ -1961,6 +2045,15 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
bool delete = false;
int err, rem;
/*
* Validate the entire listener list before making any changes, so a
* malformed request fails cleanly without creating a serv or touching
* the existing listeners.
*/
err = nfsd_nl_validate_listeners(info);
if (err)
return err;
mutex_lock(&nfsd_mutex);
err = nfsd_create_serv(net);
@@ -1987,16 +2080,11 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
const char *xcl_name;
struct sockaddr *sa;
/* validated up front in nfsd_nl_validate_listeners() */
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack) < 0)
continue;
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
continue;
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa))
continue;
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
@@ -2048,16 +2136,11 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
struct sockaddr *sa;
int ret;
/* validated up front in nfsd_nl_validate_listeners() */
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack) < 0)
continue;
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
continue;
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa))
continue;
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
@@ -2248,6 +2331,255 @@ int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info)
return 0;
}
/* Emit a single server-proc-entry nest: { op, count }. */
static int nfsd_nl_put_proc_entry(struct sk_buff *skb, int attr,
u32 op, u64 count)
{
struct nlattr *nest;
nest = nla_nest_start(skb, attr);
if (!nest)
return -EMSGSIZE;
if (nla_put_u32(skb, NFSD_A_SERVER_PROC_ENTRY_OP, op) ||
nla_put_u64_64bit(skb, NFSD_A_SERVER_PROC_ENTRY_COUNT,
count, NFSD_A_SERVER_PROC_ENTRY_PAD)) {
nla_nest_cancel(skb, nest);
return -EMSGSIZE;
}
nla_nest_end(skb, nest);
return 0;
}
/* Emit the scalar server-stats counters. Only ever called on a fresh skb. */
static int nfsd_nl_server_stats_scalars(struct sk_buff *skb,
struct nfsd_net *nn,
struct svc_stat *statp)
{
if (nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_RC_HITS,
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_RC_HITS]),
NFSD_A_SERVER_STATS_PAD) ||
nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_RC_MISSES,
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_RC_MISSES]),
NFSD_A_SERVER_STATS_PAD) ||
nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_RC_NOCACHE,
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_RC_NOCACHE]),
NFSD_A_SERVER_STATS_PAD))
return -EMSGSIZE;
if (nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_FH_STALE,
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_FH_STALE]),
NFSD_A_SERVER_STATS_PAD))
return -EMSGSIZE;
if (nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_IO_READ,
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_IO_READ]),
NFSD_A_SERVER_STATS_PAD) ||
nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_IO_WRITE,
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_IO_WRITE]),
NFSD_A_SERVER_STATS_PAD))
return -EMSGSIZE;
if (nla_put_u32(skb, NFSD_A_SERVER_STATS_NETCNT, statp->netcnt) ||
nla_put_u32(skb, NFSD_A_SERVER_STATS_NETUDPCNT, statp->netudpcnt) ||
nla_put_u32(skb, NFSD_A_SERVER_STATS_NETTCPCNT, statp->nettcpcnt) ||
nla_put_u32(skb, NFSD_A_SERVER_STATS_NETTCPCONN, statp->nettcpconn))
return -EMSGSIZE;
if (nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCCNT, statp->rpccnt) ||
nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCBADFMT, statp->rpcbadfmt) ||
nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCBADAUTH, statp->rpcbadauth) ||
nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCBADCLNT, statp->rpcbadclnt))
return -EMSGSIZE;
return 0;
}
/*
* Emit per-version procedure counts for one NFS version, resuming at *idx.
* Returns 0 when the version has been fully emitted (or is not present), or
* -EMSGSIZE when @skb filled up, leaving *idx at the entry still to emit.
*/
static int nfsd_nl_server_stats_proc(struct sk_buff *skb,
struct svc_stat *statp,
struct svc_program *prog,
unsigned int ver, int attr, int *idx)
{
unsigned long __percpu *counts;
unsigned int nproc;
if (!statp->vs_count || ver >= prog->pg_nvers ||
!prog->pg_vers[ver] || !statp->vs_count[ver])
return 0;
counts = statp->vs_count[ver];
nproc = prog->pg_vers[ver]->vs_nproc;
for (; *idx < nproc; (*idx)++) {
unsigned long count = 0;
int cpu;
for_each_possible_cpu(cpu)
count += per_cpu(counts[*idx], cpu);
if (!count)
continue;
if (nfsd_nl_put_proc_entry(skb, attr, *idx, count))
return -EMSGSIZE;
}
return 0;
}
#ifdef CONFIG_NFSD_V4
/*
* Emit NFSv4 per-operation counts, resuming at *idx. Same return convention
* as nfsd_nl_server_stats_proc().
*/
static int nfsd_nl_server_stats_nfs4ops(struct sk_buff *skb,
struct nfsd_net *nn, int *idx)
{
for (; *idx <= LAST_NFS4_OP; (*idx)++) {
u64 cnt = percpu_counter_sum_positive(
&nn->counter[NFSD_STATS_NFS4_OP(*idx)]);
if (!cnt)
continue;
if (nfsd_nl_put_proc_entry(skb, NFSD_A_SERVER_STATS_PROC4OPS_OPS,
*idx, cnt))
return -EMSGSIZE;
}
return 0;
}
/*
* Emit NFSv4 callback (backchannel) per-operation counts, resuming at *idx,
* which counts from OP_CB_GETATTR. Same return convention as
* nfsd_nl_server_stats_proc().
*/
static int nfsd_nl_server_stats_cbops(struct sk_buff *skb,
struct nfsd_net *nn, int *idx)
{
int op;
for (op = OP_CB_GETATTR + *idx; op <= OP_CB_OFFLOAD; op++, (*idx)++) {
u64 cnt = percpu_counter_sum_positive(&nn->cb_counter[op]);
if (!cnt)
continue;
if (nfsd_nl_put_proc_entry(skb, NFSD_A_SERVER_STATS_PROC4CB_OPS,
op, cnt))
return -EMSGSIZE;
}
return 0;
}
#endif
/* Sections of the server-stats dump, emitted in order across messages. */
enum {
NFSD_SERVER_STATS_SCALARS = 0,
NFSD_SERVER_STATS_PROC2,
NFSD_SERVER_STATS_PROC3,
NFSD_SERVER_STATS_PROC4,
NFSD_SERVER_STATS_PROC4CB,
NFSD_SERVER_STATS_PROC4OPS,
NFSD_SERVER_STATS_DONE,
};
/**
* nfsd_nl_server_stats_get_dumpit - dump NFS server statistics
* @skb: reply buffer
* @cb: netlink metadata and command arguments
*
* The server-stats object is emitted across one or more netlink messages.
* cb->args[0] tracks the current section and cb->args[1] the entry index
* within it, so a section that does not fit in the current message is resumed
* in the next one. The scalar counters are small and emitted once, in the
* first message; userspace merges the attributes from every message.
*
* Returns the size of the reply or a negative errno.
*/
int nfsd_nl_server_stats_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_stat *statp = &nn->nfsd_svcstats;
struct svc_program *prog = statp->program;
int section = cb->args[0];
int idx = cb->args[1];
void *hdr;
if (section >= NFSD_SERVER_STATS_DONE)
return 0;
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, &nfsd_nl_family,
NLM_F_MULTI, NFSD_CMD_SERVER_STATS_GET);
if (!hdr)
return -ENOBUFS;
/* Scalar stats fit easily and are emitted in the first message. */
if (section == NFSD_SERVER_STATS_SCALARS) {
if (nfsd_nl_server_stats_scalars(skb, nn, statp))
goto err_cancel;
section = NFSD_SERVER_STATS_PROC2;
idx = 0;
}
/*
* Emit as many of the remaining sections as fit. A section returning
* -EMSGSIZE means the message is full: close it and resume from the
* same section/index on the next call with a fresh skb. Each entry is
* small enough to fit in a fresh skb, so forward progress is assured.
*/
while (section < NFSD_SERVER_STATS_DONE) {
int ret = 0;
switch (section) {
case NFSD_SERVER_STATS_PROC2:
ret = nfsd_nl_server_stats_proc(skb, statp, prog, 2,
NFSD_A_SERVER_STATS_PROC2_OPS, &idx);
break;
case NFSD_SERVER_STATS_PROC3:
ret = nfsd_nl_server_stats_proc(skb, statp, prog, 3,
NFSD_A_SERVER_STATS_PROC3_OPS, &idx);
break;
case NFSD_SERVER_STATS_PROC4:
ret = nfsd_nl_server_stats_proc(skb, statp, prog, 4,
NFSD_A_SERVER_STATS_PROC4_OPS, &idx);
break;
#ifdef CONFIG_NFSD_V4
case NFSD_SERVER_STATS_PROC4CB:
ret = nfsd_nl_server_stats_cbops(skb, nn, &idx);
break;
case NFSD_SERVER_STATS_PROC4OPS:
ret = nfsd_nl_server_stats_nfs4ops(skb, nn, &idx);
break;
#endif
}
if (ret == -EMSGSIZE)
goto out;
if (ret)
goto err_cancel;
section++;
idx = 0;
}
out:
genlmsg_end(skb, hdr);
cb->args[0] = section;
cb->args[1] = idx;
return skb->len;
err_cancel:
genlmsg_cancel(skb, hdr);
return -EMSGSIZE;
}
int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_type)
{
struct genlmsghdr *hdr;
@@ -2343,7 +2675,7 @@ int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb,
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
mutex_lock(&nfsd_mutex);
if (nn->nfsd_serv) {
if (test_bit(NFSD_NET_UP, &nn->flags)) {
nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb);
nfsd4_revoke_states(nn, path.dentry->d_sb);
} else {
@@ -2390,7 +2722,7 @@ int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info)
return error;
mutex_lock(&nfsd_mutex);
if (nn->nfsd_serv) {
if (test_bit(NFSD_NET_UP, &nn->flags)) {
nfsd_file_close_export(net, &path);
nfsd4_revoke_export_states(nn, &path);
} else
@@ -2431,11 +2763,21 @@ static __net_init int nfsd_net_init(struct net *net)
if (retval)
goto out_repcache_error;
#ifdef CONFIG_NFSD_V4
retval = percpu_counter_init_many(nn->cb_counter, 0, GFP_KERNEL,
NFSD_STATS_CB_OPS_NUM);
if (retval)
goto out_cb_counter_error;
#endif
memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats));
nn->nfsd_svcstats.program = &nfsd_programs[0];
retval = svc_stat_alloc_counts(&nn->nfsd_svcstats);
if (retval)
goto out_proc_error;
if (!nfsd_proc_stat_init(net)) {
retval = -ENOMEM;
goto out_proc_error;
goto out_svcstats_error;
}
for (i = 0; i < sizeof(nn->nfsd_versions); i++)
@@ -2453,7 +2795,13 @@ static __net_init int nfsd_net_init(struct net *net)
#endif
return 0;
out_svcstats_error:
svc_stat_free_counts(&nn->nfsd_svcstats);
out_proc_error:
#ifdef CONFIG_NFSD_V4
percpu_counter_destroy_many(nn->cb_counter, NFSD_STATS_CB_OPS_NUM);
out_cb_counter_error:
#endif
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
out_repcache_error:
nfsd_idmap_shutdown(net);
@@ -2493,6 +2841,10 @@ static __net_exit void nfsd_net_exit(struct net *net)
kfree_sensitive(nn->fh_key);
nfsd_net_cb_shutdown(nn);
nfsd_proc_stat_shutdown(net);
svc_stat_free_counts(&nn->nfsd_svcstats);
#ifdef CONFIG_NFSD_V4
percpu_counter_destroy_many(nn->cb_counter, NFSD_STATS_CB_OPS_NUM);
#endif
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
nfsd_idmap_shutdown(net);
nfsd_export_shutdown(net);
@@ -2512,11 +2864,12 @@ static int __init init_nfsd(void)
{
int retval;
nfsd_debugfs_init();
retval = nfsd4_init_slabs();
if (retval)
return retval;
nfsd_debugfs_init();
retval = nfsd4_init_pnfs();
if (retval)
goto out_free_slabs;
@@ -2561,8 +2914,8 @@ static int __init init_nfsd(void)
out_free_pnfs:
nfsd4_exit_pnfs();
out_free_slabs:
nfsd4_free_slabs();
nfsd_debugfs_exit();
nfsd4_free_slabs();
return retval;
}
@@ -2577,9 +2930,9 @@ static void __exit exit_nfsd(void)
unregister_pernet_subsys(&nfsd_net_ops);
nfsd_drc_slab_free();
nfsd_lockd_shutdown();
nfsd4_free_slabs();
nfsd4_exit_pnfs();
nfsd_debugfs_exit();
nfsd4_free_slabs();
}
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");

View File

@@ -18,13 +18,9 @@
#include <linux/nfs4.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/addr.h>
#include <uapi/linux/nfsd/debug.h>
#include "export.h"
#undef ifdebug
#ifdef CONFIG_SUNRPC_DEBUG
# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag)
@@ -40,9 +36,6 @@
#define NFSD_SUPPORTED_MINOR_VERSION 2
bool nfsd_support_version(int vers);
#include "netns.h"
#include "stats.h"
/*
* Default and maximum payload size (NFS READ or WRITE), in bytes.
* The maximum is an implementation limit.
@@ -52,10 +45,6 @@ enum {
NFSSVC_MAXBLKSIZE = RPCSVC_MAXPAYLOAD,
};
struct readdir_cd {
__be32 err; /* 0, nfserr, or nfserr_eof */
};
/* Maximum number of operations per session compound */
#define NFSD_MAX_OPS_PER_COMPOUND 200
@@ -101,6 +90,8 @@ struct nfsdfs_client {
void (*cl_release)(struct kref *kref);
};
struct nfsd_net;
struct nfsdfs_client *get_nfsdfs_client(struct inode *);
struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
struct nfsdfs_client *ncl, u32 id,
@@ -126,8 +117,6 @@ extern const struct svc_version nfsd_acl_version3;
extern const struct svc_version localio_version1;
#endif
struct nfsd_net;
enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
@@ -162,12 +151,6 @@ static inline int nfsd_v4client(struct svc_rqst *rq)
{
return rq && rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
}
static inline struct user_namespace *
nfsd_user_namespace(const struct svc_rqst *rqstp)
{
const struct cred *cred = rqstp->rq_xprt->xpt_cred;
return cred ? cred->user_ns : &init_user_ns;
}
/*
* NFSv4 State
@@ -332,13 +315,14 @@ void nfsd_lockd_shutdown(void);
#define nfserr_noxattr cpu_to_be32(NFS4ERR_NOXATTR)
/*
* Error codes for internal use. We use enum to choose numbers that are
* not already assigned, then covert to be32 resulting in a number that
* cannot conflict with any existing be32 nfserr value.
* Error codes for internal use. These are based at an impossible
* nfsstat4 value so that, once converted to be32, they cannot conflict
* with any value defined by the protocol (compare the nlm__int__* codes
* in fs/lockd/lockd.h).
*/
enum {
/* end-of-file indicator in readdir */
NFSERR_EOF = NFS4ERR_FIRST_FREE,
NFSERR_EOF = 30000,
#define nfserr_eof cpu_to_be32(NFSERR_EOF)
/* replay detected */
@@ -356,9 +340,6 @@ enum {
#define nfserr_symlink_not_dir cpu_to_be32(NFSERR_SYMLINK_NOT_DIR)
};
/* Check for dir entries '.' and '..' */
#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
#ifdef CONFIG_NFSD_V4
/* before processing a COMPOUND operation, we have to check that there
@@ -386,187 +367,6 @@ enum {
#define NFSD_DELEGRETURN_TIMEOUT (HZ / 34) /* 30ms */
#define NFSD_CB_GETATTR_TIMEOUT NFSD_DELEGRETURN_TIMEOUT
/*
* The following attributes are not implemented by NFSD:
* ARCHIVE (deprecated anyway)
* HIDDEN (unlikely to be supported any time soon)
* MIMETYPE (unlikely to be supported any time soon)
* QUOTA_* (will be supported in a forthcoming patch)
* SYSTEM (unlikely to be supported any time soon)
* TIME_BACKUP (unlikely to be supported any time soon)
*/
#define NFSD4_SUPPORTED_ATTRS_WORD0 \
(FATTR4_WORD0_SUPPORTED_ATTRS | FATTR4_WORD0_TYPE | FATTR4_WORD0_FH_EXPIRE_TYPE \
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE | FATTR4_WORD0_LINK_SUPPORT \
| FATTR4_WORD0_SYMLINK_SUPPORT | FATTR4_WORD0_NAMED_ATTR | FATTR4_WORD0_FSID \
| FATTR4_WORD0_UNIQUE_HANDLES | FATTR4_WORD0_LEASE_TIME | FATTR4_WORD0_RDATTR_ERROR \
| FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME | FATTR4_WORD0_CASE_INSENSITIVE \
| FATTR4_WORD0_CASE_PRESERVING | FATTR4_WORD0_CHOWN_RESTRICTED \
| FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FILEID | FATTR4_WORD0_FILES_AVAIL \
| FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_HOMOGENEOUS \
| FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \
| FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL)
#define NFSD4_SUPPORTED_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_NO_TRUNC | FATTR4_WORD1_NUMLINKS \
| FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \
| FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \
| FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \
| FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_CREATE \
| FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
#define NFSD4_SUPPORTED_ATTRS_WORD2 0
/* 4.1 */
#ifdef CONFIG_NFSD_PNFS
#define PNFSD_SUPPORTED_ATTRS_WORD1 FATTR4_WORD1_FS_LAYOUT_TYPES
#define PNFSD_SUPPORTED_ATTRS_WORD2 \
(FATTR4_WORD2_LAYOUT_BLKSIZE | FATTR4_WORD2_LAYOUT_TYPES)
#else
#define PNFSD_SUPPORTED_ATTRS_WORD1 0
#define PNFSD_SUPPORTED_ATTRS_WORD2 0
#endif /* CONFIG_NFSD_PNFS */
#define NFSD4_1_SUPPORTED_ATTRS_WORD0 \
NFSD4_SUPPORTED_ATTRS_WORD0
#define NFSD4_1_SUPPORTED_ATTRS_WORD1 \
(NFSD4_SUPPORTED_ATTRS_WORD1 | PNFSD_SUPPORTED_ATTRS_WORD1)
#define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
(NFSD4_SUPPORTED_ATTRS_WORD2 | PNFSD_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_SUPPATTR_EXCLCREAT)
/* 4.2 */
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define NFSD4_2_SECURITY_ATTRS FATTR4_WORD2_SECURITY_LABEL
#else
#define NFSD4_2_SECURITY_ATTRS 0
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
#define NFSD4_2_POSIX_ACL_ATTRS \
(FATTR4_WORD2_ACL_TRUEFORM | \
FATTR4_WORD2_ACL_TRUEFORM_SCOPE | \
FATTR4_WORD2_POSIX_DEFAULT_ACL | \
FATTR4_WORD2_POSIX_ACCESS_ACL)
#else
#define NFSD4_2_POSIX_ACL_ATTRS 0
#endif
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_MODE_UMASK | \
FATTR4_WORD2_CLONE_BLKSIZE | \
NFSD4_2_SECURITY_ATTRS | \
FATTR4_WORD2_XATTR_SUPPORT | \
FATTR4_WORD2_TIME_DELEG_ACCESS | \
FATTR4_WORD2_TIME_DELEG_MODIFY | \
FATTR4_WORD2_OPEN_ARGUMENTS | \
NFSD4_2_POSIX_ACL_ATTRS)
extern const u32 nfsd_suppattrs[3][3];
static inline __be32 nfsd4_set_netaddr(struct sockaddr *addr,
struct nfs42_netaddr *netaddr)
{
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
unsigned int port;
size_t ret_addr, ret_port;
switch (addr->sa_family) {
case AF_INET:
port = ntohs(sin->sin_port);
sprintf(netaddr->netid, "tcp");
netaddr->netid_len = 3;
break;
case AF_INET6:
port = ntohs(sin6->sin6_port);
sprintf(netaddr->netid, "tcp6");
netaddr->netid_len = 4;
break;
default:
return nfserr_inval;
}
ret_addr = rpc_ntop(addr, netaddr->addr, sizeof(netaddr->addr));
ret_port = snprintf(netaddr->addr + ret_addr,
RPCBIND_MAXUADDRLEN + 1 - ret_addr,
".%u.%u", port >> 8, port & 0xff);
WARN_ON(ret_port >= RPCBIND_MAXUADDRLEN + 1 - ret_addr);
netaddr->addr_len = ret_addr + ret_port;
return 0;
}
static inline bool bmval_is_subset(const u32 *bm1, const u32 *bm2)
{
return !((bm1[0] & ~bm2[0]) ||
(bm1[1] & ~bm2[1]) ||
(bm1[2] & ~bm2[2]));
}
static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
{
return bmval_is_subset(bmval, nfsd_suppattrs[minorversion]);
}
/* These will return ERR_INVAL if specified in GETATTR or READDIR. */
#define NFSD_WRITEONLY_ATTRS_WORD1 \
(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
/*
* These are the only attrs allowed in CREATE/OPEN/SETATTR. Don't add
* a writeable attribute here without also adding code to parse it to
* nfsd4_decode_fattr().
*/
#define NFSD_WRITEABLE_ATTRS_WORD0 \
(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
#define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_CREATE \
| FATTR4_WORD1_TIME_MODIFY_SET)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL \
FATTR4_WORD2_SECURITY_LABEL
#else
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL 0
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \
FATTR4_WORD2_POSIX_DEFAULT_ACL | FATTR4_WORD2_POSIX_ACCESS_ACL
#else
#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS 0
#endif
#define NFSD_WRITEABLE_ATTRS_WORD2 \
(FATTR4_WORD2_MODE_UMASK \
| MAYBE_FATTR4_WORD2_SECURITY_LABEL \
| FATTR4_WORD2_TIME_DELEG_ACCESS \
| FATTR4_WORD2_TIME_DELEG_MODIFY \
| MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \
)
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
NFSD_WRITEABLE_ATTRS_WORD0
/*
* we currently store the exclusive create verifier in the v_{a,m}time
* attributes so the client can't set these at create time using EXCLUSIVE4_1
*/
#define NFSD_SUPPATTR_EXCLCREAT_WORD1 \
(NFSD_WRITEABLE_ATTRS_WORD1 & \
~(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET))
/*
* The FATTR4_WORD2_TIME_DELEG attributes are not to be allowed for
* OPEN(create) with EXCLUSIVE4_1. It doesn't make sense to set a
* delegated timestamp on a new file.
*
* This mask includes NFSv4.2-only attributes (e.g., POSIX ACLs).
* Version filtering occurs via nfsd_suppattrs[] before this mask
* is applied, so pre-4.2 clients never see unsupported attributes.
*/
#define NFSD_SUPPATTR_EXCLCREAT_WORD2 \
(NFSD_WRITEABLE_ATTRS_WORD2 & \
~(FATTR4_WORD2_TIME_DELEG_ACCESS | FATTR4_WORD2_TIME_DELEG_MODIFY))
extern int nfsd4_is_junction(struct dentry *dentry);
extern int register_cld_notifier(void);
extern void unregister_cld_notifier(void);

View File

@@ -13,6 +13,8 @@
#include <linux/sunrpc/svcauth_gss.h>
#include <crypto/utils.h>
#include "nfsd.h"
#include "netns.h"
#include "stats.h"
#include "vfs.h"
#include "auth.h"
#include "trace.h"
@@ -70,10 +72,8 @@ nfsd_mode_check(struct dentry *dentry, umode_t requested)
if (requested == 0) /* the caller doesn't care */
return nfs_ok;
if (mode == requested) {
if (mode == S_IFDIR && !d_can_lookup(dentry)) {
WARN_ON_ONCE(1);
if (mode == S_IFDIR && !d_can_lookup(dentry))
return nfserr_notdir;
}
return nfs_ok;
}
if (mode == S_IFLNK) {
@@ -144,16 +144,15 @@ static inline __be32 check_pseudo_root(struct dentry *dentry,
/* Size of a file handle MAC, in 4-octet words */
#define FH_MAC_WORDS (sizeof(__le64) / 4)
static bool fh_append_mac(struct svc_fh *fhp, struct net *net)
bool fh_append_mac(struct knfsd_fh *fh, int fh_maxsize, struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct knfsd_fh *fh = &fhp->fh_handle;
siphash_key_t *fh_key = nn->fh_key;
__le64 hash;
if (!fh_key)
goto out_no_key;
if (fh->fh_size + sizeof(hash) > fhp->fh_maxsize)
if (fh->fh_size + sizeof(hash) > fh_maxsize)
goto out_no_space;
hash = cpu_to_le64(siphash(&fh->fh_raw, fh->fh_size, fh_key));
@@ -167,7 +166,7 @@ static bool fh_append_mac(struct svc_fh *fhp, struct net *net)
out_no_space:
pr_warn_ratelimited("NFSD: unable to sign filehandles, fh_size %zu would be greater than fh_maxsize %d.\n",
fh->fh_size + sizeof(hash), fhp->fh_maxsize);
fh->fh_size + sizeof(hash), fh_maxsize);
return false;
}
@@ -344,15 +343,19 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net,
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC)
fhp->fh_no_wcc = true;
fhp->fh_64bit_cookies = true;
if (exp->ex_flags & NFSEXP_V4ROOT)
if (exp->ex_flags & NFSEXP_V4ROOT) {
dput(dentry);
goto out;
}
break;
case NFS_FHSIZE:
fhp->fh_no_wcc = true;
if (EX_WGATHER(exp))
fhp->fh_use_wgather = true;
if (exp->ex_flags & NFSEXP_V4ROOT)
if (exp->ex_flags & NFSEXP_V4ROOT) {
dput(dentry);
goto out;
}
}
fhp->fh_dentry = dentry;
@@ -562,7 +565,8 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
fhp->fh_handle.fh_size += maxsize * 4;
if (exp->ex_flags & NFSEXP_SIGN_FH)
if (!fh_append_mac(fhp, exp->cd->net))
if (!fh_append_mac(&fhp->fh_handle, fhp->fh_maxsize,
exp->cd->net))
fhp->fh_handle.fh_fileid_type = FILEID_INVALID;
} else {
fhp->fh_handle.fh_fileid_type = FILEID_ROOT;
@@ -892,19 +896,20 @@ char * SVCFH_fmt(struct svc_fh *fhp)
return buf;
}
enum fsid_source fsid_source(const struct svc_fh *fhp)
enum fsid_source fsid_source_fh(const struct knfsd_fh *fh,
struct svc_export *exp)
{
if (fhp->fh_handle.fh_version != 1)
if (fh->fh_version != 1)
return FSIDSOURCE_DEV;
switch(fhp->fh_handle.fh_fsid_type) {
switch (fh->fh_fsid_type) {
case FSID_DEV:
case FSID_ENCODE_DEV:
case FSID_MAJOR_MINOR:
if (exp_sb(fhp->fh_export)->s_type->fs_flags & FS_REQUIRES_DEV)
if (exp_sb(exp)->s_type->fs_flags & FS_REQUIRES_DEV)
return FSIDSOURCE_DEV;
break;
case FSID_NUM:
if (fhp->fh_export->ex_flags & NFSEXP_FSID)
if (exp->ex_flags & NFSEXP_FSID)
return FSIDSOURCE_FSID;
break;
default:
@@ -913,13 +918,18 @@ enum fsid_source fsid_source(const struct svc_fh *fhp)
/* either a UUID type filehandle, or the filehandle doesn't
* match the export.
*/
if (fhp->fh_export->ex_flags & NFSEXP_FSID)
if (exp->ex_flags & NFSEXP_FSID)
return FSIDSOURCE_FSID;
if (fhp->fh_export->ex_uuid)
if (exp->ex_uuid)
return FSIDSOURCE_UUID;
return FSIDSOURCE_DEV;
}
enum fsid_source fsid_source(const struct svc_fh *fhp)
{
return fsid_source_fh(&fhp->fh_handle, fhp->fh_export);
}
/**
* nfsd4_change_attribute - Generate an NFSv4 change_attribute value
* @stat: inode attributes

View File

@@ -131,6 +131,8 @@ enum fsid_source {
FSIDSOURCE_FSID,
FSIDSOURCE_UUID,
};
extern enum fsid_source fsid_source_fh(const struct knfsd_fh *fh,
struct svc_export *exp);
extern enum fsid_source fsid_source(const struct svc_fh *fhp);
@@ -226,6 +228,7 @@ __be32 fh_getattr(const struct svc_fh *fhp, struct kstat *stat);
__be32 fh_compose(struct svc_fh *, struct svc_export *, struct dentry *, struct svc_fh *);
__be32 fh_update(struct svc_fh *);
void fh_put(struct svc_fh *);
bool fh_append_mac(struct knfsd_fh *fh, int fh_maxsize, struct net *net);
static __inline__ struct svc_fh *
fh_copy(struct svc_fh *dst, const struct svc_fh *src)

View File

@@ -82,6 +82,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
.na_iattr = iap,
};
struct svc_fh *fhp;
int hosterr;
dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
SVCFH_fmt(&argp->fh),
@@ -117,6 +118,12 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
if (resp->status != nfs_ok)
goto out;
hosterr = fh_want_write(fhp);
if (hosterr) {
resp->status = nfserrno(hosterr);
goto out;
}
if (delta < 0)
delta = -delta;
if (delta < MAX_TOUCH_TIME_ERROR &&
@@ -298,7 +305,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
/* Check for NFSD_MAY_WRITE in nfsd_create if necessary */
resp->status = nfserr_exist;
if (isdotent(argp->name, argp->len))
if (name_is_dot_dotdot(argp->name, argp->len))
goto done;
hosterr = fh_want_write(dirfhp);
if (hosterr) {
@@ -838,13 +845,10 @@ static const struct svc_procedure nfsd_procedures2[18] = {
},
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nfsd_count2[ARRAY_SIZE(nfsd_procedures2)]);
const struct svc_version nfsd_version2 = {
.vs_vers = 2,
.vs_nproc = ARRAY_SIZE(nfsd_procedures2),
.vs_proc = nfsd_procedures2,
.vs_count = nfsd_count2,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS2_SVC_XDRSIZE,
};

View File

@@ -237,15 +237,21 @@ static void nfsd_net_free(struct percpu_ref *ref)
*/
#define NFSD_MAXSERVS 8192
/**
* nfsd_nrthreads - report a namespace's configured nfsd thread count
* @net: network namespace to query
*
* Return: the configured thread ceiling, or 0 when no service runs.
*/
int nfsd_nrthreads(struct net *net)
{
int i, rv = 0;
int rv = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
/* nfsd_mutex keeps nn->nfsd_serv valid across the read. */
mutex_lock(&nfsd_mutex);
if (nn->nfsd_serv)
for (i = 0; i < nn->nfsd_serv->sv_nrpools; ++i)
rv += nn->nfsd_serv->sv_pools[i].sp_nrthrmax;
rv = svc_serv_maxthreads(nn->nfsd_serv);
mutex_unlock(&nfsd_mutex);
return rv;
}
@@ -351,7 +357,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
int ret;
if (nn->nfsd_net_up)
if (test_bit(NFSD_NET_UP, &nn->flags))
return 0;
ret = nfsd_startup_generic();
@@ -364,11 +370,11 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
goto out_socks;
}
if (nfsd_needs_lockd(nn) && !nn->lockd_up) {
if (nfsd_needs_lockd(nn) && !test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) {
ret = lockd_up(net, cred);
if (ret)
goto out_socks;
nn->lockd_up = true;
set_bit(NFSD_NET_LOCKD_UP, &nn->flags);
}
ret = nfsd_file_cache_start_net(net);
@@ -386,7 +392,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
if (ret)
goto out_reply_cache;
nn->nfsd_net_up = true;
set_bit(NFSD_NET_UP, &nn->flags);
return 0;
out_reply_cache:
@@ -394,9 +400,9 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
out_filecache:
nfsd_file_cache_shutdown_net(net);
out_lockd:
if (nn->lockd_up) {
if (test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) {
lockd_down(net);
nn->lockd_up = false;
clear_bit(NFSD_NET_LOCKD_UP, &nn->flags);
}
out_socks:
nfsd_shutdown_generic();
@@ -407,7 +413,7 @@ static void nfsd_shutdown_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (nn->nfsd_net_up) {
if (test_bit(NFSD_NET_UP, &nn->flags)) {
percpu_ref_kill_and_confirm(&nn->nfsd_net_ref, nfsd_net_done);
wait_for_completion(&nn->nfsd_net_confirm_done);
@@ -415,18 +421,18 @@ static void nfsd_shutdown_net(struct net *net)
nfs4_state_shutdown_net(net);
nfsd_reply_cache_shutdown(nn);
nfsd_file_cache_shutdown_net(net);
if (nn->lockd_up) {
if (test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) {
lockd_down(net);
nn->lockd_up = false;
clear_bit(NFSD_NET_LOCKD_UP, &nn->flags);
}
wait_for_completion(&nn->nfsd_net_free_done);
}
percpu_ref_exit(&nn->nfsd_net_ref);
if (nn->nfsd_net_up)
if (test_bit(NFSD_NET_UP, &nn->flags))
nfsd_shutdown_generic();
nn->nfsd_net_up = false;
clear_bit(NFSD_NET_UP, &nn->flags);
}
static DEFINE_SPINLOCK(nfsd_notifier_lock);
@@ -649,7 +655,7 @@ int nfsd_nrpools(struct net *net)
if (nn->nfsd_serv == NULL)
return 0;
else
return nn->nfsd_serv->sv_nrpools;
return svc_serv_nrpools(nn->nfsd_serv);
}
int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
@@ -659,7 +665,7 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
int i;
if (serv)
for (i = 0; i < serv->sv_nrpools && i < n; i++)
for (i = 0; i < svc_serv_nrpools(serv) && i < n; i++)
nthreads[i] = serv->sv_pools[i].sp_nrthrmax;
return 0;
}
@@ -693,8 +699,8 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
if (n == 1)
return svc_set_num_threads(nn->nfsd_serv, nn->min_threads, nthreads[0]);
if (n > nn->nfsd_serv->sv_nrpools)
n = nn->nfsd_serv->sv_nrpools;
if (n > svc_serv_nrpools(nn->nfsd_serv))
n = svc_serv_nrpools(nn->nfsd_serv);
/* enforce a global maximum number of threads */
tot = 0;
@@ -725,7 +731,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
}
/* Anything undefined in array is considered to be 0 */
for (i = n; i < nn->nfsd_serv->sv_nrpools; ++i) {
for (i = n; i < svc_serv_nrpools(nn->nfsd_serv); ++i) {
err = svc_set_pool_threads(nn->nfsd_serv,
&nn->nfsd_serv->sv_pools[i],
0, 0);
@@ -815,7 +821,7 @@ nfsd_acl_init_request(struct svc_rqst *rqstp,
ret->mismatch.lovers = NFSD_ACL_NRVERS;
for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) {
if (nfsd_support_acl_version(rqstp->rq_vers) &&
if (nfsd_support_acl_version(i) &&
nfsd_vers(nn, i, NFSD_TEST)) {
ret->mismatch.lovers = i;
break;
@@ -825,7 +831,7 @@ nfsd_acl_init_request(struct svc_rqst *rqstp,
return rpc_prog_unavail;
ret->mismatch.hivers = NFSD_ACL_MINVERS;
for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) {
if (nfsd_support_acl_version(rqstp->rq_vers) &&
if (nfsd_support_acl_version(i) &&
nfsd_vers(nn, i, NFSD_TEST)) {
ret->mismatch.hivers = i;
break;
@@ -960,6 +966,20 @@ nfsd(void *vrqstp)
return 0;
}
/*
* Set rq_status_counter back to an even value, indicating that the rqstp
* fields are no longer meaningful to a lockless reader. This pairs with the
* odd-valued store made once the request has been decoded, and must run on
* every return path that follows it so that the seq-lock like protocol used
* by nfsd_nl_rpc_status_get_dumpit() is not left permanently odd. The store
* also advances the counter so a concurrent reader detects the transition.
*/
static void nfsd_status_counter_set_idle(struct svc_rqst *rqstp)
{
smp_store_release(&rqstp->rq_status_counter,
(rqstp->rq_status_counter | 1) + 1);
}
/**
* nfsd_dispatch - Process an NFS or NFSACL or LOCALIO Request
* @rqstp: incoming request
@@ -1022,14 +1042,9 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
goto out_encode_err;
/*
* Release rq_status_counter setting it to an even value after the rpc
* request has been properly processed.
*/
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1);
nfsd_cache_update(rqstp, rp, ntli->ntli_cachetype, nfs_reply);
out_cached_reply:
nfsd_status_counter_set_idle(rqstp);
return 1;
out_decode_err:
@@ -1040,12 +1055,14 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
out_update_drop:
nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
out_dropit:
nfsd_status_counter_set_idle(rqstp);
return 0;
out_encode_err:
trace_nfsd_cant_encode_err(rqstp);
nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
*statp = rpc_system_err;
nfsd_status_counter_set_idle(rqstp);
return 1;
}

View File

@@ -5,10 +5,22 @@
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/filelock.h>
#include "vfs.h"
#include "xdr.h"
#include "auth.h"
/*
* Sun convention: a sattr time-useconds field of one full second (an
* otherwise out-of-range value) means "set this time to the current
* server time." It's needed to make permissions checks for the "touch"
* program across NFSv2 mounts work correctly. See description of
* sattr in section 6.1 of "NFS Illustrated" by Brent Callaghan,
* Addison-Wesley, ISBN 0-201-32750-5
*/
#define NFS2_SATTR_SET_TO_SERVER_TIME (1000000)
/*
* Mapping of S_IF* types to NFS file types
*/
@@ -172,27 +184,29 @@ svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
tmp1 = be32_to_cpup(p++);
tmp2 = be32_to_cpup(p++);
if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
/*
* Range test here to prevent the multiplication from
* wrapping to a valid (but incorrect) value on 32-bit
* platforms.
*/
if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME)
return false;
iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
iap->ia_atime.tv_sec = tmp1;
iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME)
iap->ia_valid &= ~ATTR_ATIME_SET;
}
tmp1 = be32_to_cpup(p++);
tmp2 = be32_to_cpup(p++);
if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME)
return false;
iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
iap->ia_mtime.tv_sec = tmp1;
iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
/*
* Passing the invalid value useconds=1000000 for mtime
* is a Sun convention for "set both mtime and atime to
* current server time". It's needed to make permissions
* checks for the "touch" program across v2 mounts to
* Solaris and Irix boxes work correctly. See description of
* sattr in section 6.1 of "NFS Illustrated" by
* Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
*/
if (tmp2 == 1000000)
if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME)
iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
}

View File

@@ -36,9 +36,12 @@
#define _NFSD4_STATE_H
#include <crypto/md5.h>
#include <linux/filelock.h>
#include <linux/idr.h>
#include <linux/refcount.h>
#include <linux/sunrpc/svc_xprt.h>
#include "nfsfh.h"
#include "nfsd.h"
@@ -59,7 +62,6 @@ typedef struct {
typedef struct {
stateid_t cs_stid;
#define NFS4_COPY_STID 1
#define NFS4_COPYNOTIFY_STID 2
unsigned char cs_type;
refcount_t cs_count;
@@ -98,9 +100,9 @@ struct nfsd4_callback {
};
struct nfsd4_callback_ops {
void (*prepare)(struct nfsd4_callback *);
int (*done)(struct nfsd4_callback *, struct rpc_task *);
void (*release)(struct nfsd4_callback *);
bool (*prepare)(struct nfsd4_callback *cb);
int (*done)(struct nfsd4_callback *cb, struct rpc_task *task);
void (*release)(struct nfsd4_callback *cb);
uint32_t opcode;
};
@@ -121,6 +123,7 @@ struct nfs4_stid {
#define SC_TYPE_LOCK BIT(1)
#define SC_TYPE_DELEG BIT(2)
#define SC_TYPE_LAYOUT BIT(3)
#define SC_TYPE_COPY BIT(4)
unsigned short sc_type;
/* nn->deleg_lock protects sc_status for delegation stateids.
@@ -190,6 +193,66 @@ struct nfs4_cb_fattr {
u64 ncf_cur_fsize;
};
/*
* FIXME: the current backchannel encoder can't handle a send buffer longer
* than a single page (see bc_malloc/bc_free).
*/
#define NOTIFY4_EVENT_QUEUE_SIZE 3
#define NOTIFY4_PAGE_ARRAY_SIZE 1
struct nfsd_notify_event {
refcount_t ne_ref; // refcount
u32 ne_mask; // FS_* mask from fsnotify callback
struct dentry *ne_dentry; // dentry reference to target
struct inode *ne_target; // inode overwritten by rename, or NULL
u32 ne_namelen; // length of ne_name (old name for a rename)
u32 ne_newnamelen; // length of new name (rename only), else 0
char ne_name[]; // entry name, then new name (rename only)
};
/*
* For a rename, the new name is snapshotted at event-alloc time and stored
* immediately after the (NUL-terminated) old name in ne_name[]. ne_dentry can
* be renamed again before the CB_NOTIFY work runs, so the new name must not be
* read from the live dentry at encode time.
*/
static inline char *nfsd_notify_event_newname(struct nfsd_notify_event *ne)
{
return ne->ne_name + ne->ne_namelen + 1;
}
static inline struct nfsd_notify_event *nfsd_notify_event_get(struct nfsd_notify_event *ne)
{
refcount_inc(&ne->ne_ref);
return ne;
}
static inline void nfsd_notify_event_put(struct nfsd_notify_event *ne)
{
if (refcount_dec_and_test(&ne->ne_ref)) {
iput(ne->ne_target);
dput(ne->ne_dentry);
kfree(ne);
}
}
/*
* Represents a directory delegation. The callback is for handling CB_NOTIFYs.
* As notifications from fsnotify come in, allocate a new event, take the ncn_lock,
* and add it to the ncn_evt queue. The CB_NOTIFY prepare handler will take the
* lock, clean out the list and process it.
*/
struct nfsd4_cb_notify {
spinlock_t ncn_lock; // protects the evt queue and count
int ncn_evt_cnt; // count of events in ncn_evt
int ncn_nf_cnt; // count of valid entries in ncn_nf
struct nfsd_notify_event *ncn_evt[NOTIFY4_EVENT_QUEUE_SIZE]; // list of events
struct page *ncn_pages[NOTIFY4_PAGE_ARRAY_SIZE]; // for encoding
struct notify4 *ncn_nf; // array of notify4's to be sent
bool ncn_encode_err; // did encoding fail?
struct nfsd4_callback ncn_cb; // notify4 callback
};
/*
* Represents a delegation stateid. The nfs4_client holds references to these
* and they are put when it is being destroyed or when the delegation is
@@ -226,13 +289,22 @@ struct nfs4_delegation {
bool dl_written;
bool dl_setattr;
/* for CB_GETATTR */
struct nfs4_cb_fattr dl_cb_fattr;
union {
/* for CB_GETATTR */
struct nfs4_cb_fattr dl_cb_fattr;
/* for CB_NOTIFY */
struct nfsd4_cb_notify dl_cb_notify;
};
/* For delegated timestamps */
struct timespec64 dl_atime;
struct timespec64 dl_mtime;
struct timespec64 dl_ctime;
/* For dir delegations */
u32 dl_notify_mask;
u32 dl_child_attrs[2];
u32 dl_dir_attrs[2];
};
static inline bool deleg_is_read(u32 dl_type)
@@ -384,6 +456,7 @@ struct nfsd4_session {
u16 se_slot_gen;
bool se_dead;
u32 se_target_maxslots;
struct rcu_head rcu_head;
};
/* formatted contents of nfs4_sessionid */
@@ -496,7 +569,7 @@ struct nfs4_client {
#define NFSD4_CB_FAULT 3
int cl_cb_state;
struct nfsd4_callback cl_cb_null;
struct nfsd4_session *cl_cb_session;
struct nfsd4_session __rcu *cl_cb_session;
/* for all client information that callback code might need: */
spinlock_t cl_lock;
@@ -691,7 +764,7 @@ struct nfs4_file {
*/
atomic_t fi_access[2];
u32 fi_share_deny;
struct nfsd_file *fi_deleg_file;
struct nfsd_file __rcu *fi_deleg_file;
struct nfsd_file *fi_rdeleg_file;
int fi_delegees;
struct knfsd_fh fi_fhandle;
@@ -754,6 +827,7 @@ struct nfs4_layout_stateid {
struct delayed_work ls_fence_work;
unsigned int ls_fence_delay;
bool ls_fenced;
bool ls_fence_inflight;
};
static inline struct nfs4_layout_stateid *layoutstateid(struct nfs4_stid *s)
@@ -774,6 +848,7 @@ enum nfsd4_cb_op {
NFSPROC4_CLNT_CB_NOTIFY_LOCK,
NFSPROC4_CLNT_CB_RECALL_ANY,
NFSPROC4_CLNT_CB_GETATTR,
NFSPROC4_CLNT_CB_NOTIFY,
};
/* Returns true iff a is later than b: */
@@ -800,6 +875,7 @@ struct nfsd4_blocked_lock {
struct nfsd4_compound_state;
struct nfsd_net;
struct nfsd4_copy;
struct nfsd4_async_copy;
extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
@@ -811,8 +887,7 @@ __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
struct nfs4_stid **s, struct nfsd_net *nn);
struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
void (*sc_free)(struct nfs4_stid *));
int nfs4_init_copy_state(struct nfsd_net *nn, struct nfsd4_copy *copy);
void nfs4_free_copy_state(struct nfsd4_copy *copy);
struct nfsd4_async_copy *nfs4_alloc_copy_stid(struct nfs4_client *clp);
struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
struct nfs4_stid *p_stid);
void nfs4_put_stid(struct nfs4_stid *s);
@@ -848,6 +923,8 @@ void nfsd_update_cmtime_attr(struct file *f, unsigned int flags);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
struct xdr_netobj princhash, struct nfsd_net *nn);
extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
int nfsd_handle_dir_event(u32 mask, const struct inode *dir, const void *data,
int data_type, const struct qstr *name);
void put_nfs4_file(struct nfs4_file *fi);
extern void nfs4_put_cpntf_state(struct nfsd_net *nn,

View File

@@ -26,6 +26,8 @@
#include <net/net_namespace.h>
#include "nfsd.h"
#include "netns.h"
#include "stats.h"
static int nfsd_show(struct seq_file *seq, void *v)
{
@@ -63,7 +65,7 @@ static int nfsd_show(struct seq_file *seq, void *v)
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_NFS4_OP(i)]));
}
seq_printf(seq, "\nwdeleg_getattr %lld",
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_WDELEG_GETATTR]));
percpu_counter_sum_positive(&nn->cb_counter[OP_CB_GETATTR]));
seq_putc(seq, '\n');
#endif

View File

@@ -10,22 +10,45 @@
#include <uapi/linux/nfsd/stats.h>
#include <linux/percpu_counter.h>
#include "export.h"
#include "netns.h"
struct proc_dir_entry *nfsd_proc_stat_init(struct net *net);
void nfsd_proc_stat_shutdown(struct net *net);
/**
* nfsd_stats_rc_hits_inc - Count a duplicate reply cache hit
* @nn: target network namespace
*
* These reply cache counters are updated once per RPC. Readers use
* percpu_counter_sum_positive(), so local batching does not affect
* read accuracy.
*/
static inline void nfsd_stats_rc_hits_inc(struct nfsd_net *nn)
{
percpu_counter_inc(&nn->counter[NFSD_STATS_RC_HITS]);
percpu_counter_add_local(&nn->counter[NFSD_STATS_RC_HITS], 1);
}
/**
* nfsd_stats_rc_misses_inc - Count a duplicate reply cache miss
* @nn: target network namespace
*
* See nfsd_stats_rc_hits_inc() for batching rationale.
*/
static inline void nfsd_stats_rc_misses_inc(struct nfsd_net *nn)
{
percpu_counter_inc(&nn->counter[NFSD_STATS_RC_MISSES]);
percpu_counter_add_local(&nn->counter[NFSD_STATS_RC_MISSES], 1);
}
/**
* nfsd_stats_rc_nocache_inc - Count a request not cached in the reply cache
* @nn: target network namespace
*
* See nfsd_stats_rc_hits_inc() for batching rationale.
*/
static inline void nfsd_stats_rc_nocache_inc(struct nfsd_net *nn)
{
percpu_counter_inc(&nn->counter[NFSD_STATS_RC_NOCACHE]);
percpu_counter_add_local(&nn->counter[NFSD_STATS_RC_NOCACHE], 1);
}
static inline void nfsd_stats_fh_stale_inc(struct nfsd_net *nn,
@@ -36,20 +59,42 @@ static inline void nfsd_stats_fh_stale_inc(struct nfsd_net *nn,
percpu_counter_inc(&exp->ex_stats->counter[EXP_STATS_FH_STALE]);
}
/**
* nfsd_stats_io_read_add - Count number of bytes for an NFS READ
* @nn: target network namespace
* @exp: target export
* @amount: byte count
*
* These counters are updated on every READ request. Readers use
* percpu_counter_sum_positive(), so local batching does not affect
* read accuracy.
*/
static inline void nfsd_stats_io_read_add(struct nfsd_net *nn,
struct svc_export *exp, s64 amount)
{
percpu_counter_add(&nn->counter[NFSD_STATS_IO_READ], amount);
percpu_counter_add_local(&nn->counter[NFSD_STATS_IO_READ], amount);
if (exp && exp->ex_stats)
percpu_counter_add(&exp->ex_stats->counter[EXP_STATS_IO_READ], amount);
percpu_counter_add_local(&exp->ex_stats->counter[EXP_STATS_IO_READ],
amount);
}
/**
* nfsd_stats_io_write_add - Count number of bytes for an NFS WRITE
* @nn: target network namespace
* @exp: target export
* @amount: byte count
*
* These counters are updated on every WRITE request. Readers use
* percpu_counter_sum_positive(), so local batching does not affect
* read accuracy.
*/
static inline void nfsd_stats_io_write_add(struct nfsd_net *nn,
struct svc_export *exp, s64 amount)
{
percpu_counter_add(&nn->counter[NFSD_STATS_IO_WRITE], amount);
percpu_counter_add_local(&nn->counter[NFSD_STATS_IO_WRITE], amount);
if (exp && exp->ex_stats)
percpu_counter_add(&exp->ex_stats->counter[EXP_STATS_IO_WRITE], amount);
percpu_counter_add_local(&exp->ex_stats->counter[EXP_STATS_IO_WRITE],
amount);
}
static inline void nfsd_stats_payload_misses_inc(struct nfsd_net *nn)
@@ -57,20 +102,39 @@ static inline void nfsd_stats_payload_misses_inc(struct nfsd_net *nn)
percpu_counter_inc(&nn->counter[NFSD_STATS_PAYLOAD_MISSES]);
}
/**
* nfsd_stats_drc_mem_usage_add - Add memory used by a cache item
* @nn: target network namespace
* @amount: byte count
*
* percpu_counter_add_local() keeps updates on the per-CPU fast
* path. The sole reader, percpu_counter_sum_positive(), sums the
* per-CPU deltas, so batching locally does not lose accuracy.
*/
static inline void nfsd_stats_drc_mem_usage_add(struct nfsd_net *nn, s64 amount)
{
percpu_counter_add(&nn->counter[NFSD_STATS_DRC_MEM_USAGE], amount);
percpu_counter_add_local(&nn->counter[NFSD_STATS_DRC_MEM_USAGE],
amount);
}
/**
* nfsd_stats_drc_mem_usage_sub - Subtract memory used by a cache item
* @nn: target network namespace
* @amount: byte count
*
* See nfsd_stats_drc_mem_usage_add() for batching rationale.
*/
static inline void nfsd_stats_drc_mem_usage_sub(struct nfsd_net *nn, s64 amount)
{
percpu_counter_sub(&nn->counter[NFSD_STATS_DRC_MEM_USAGE], amount);
percpu_counter_sub_local(&nn->counter[NFSD_STATS_DRC_MEM_USAGE],
amount);
}
#ifdef CONFIG_NFSD_V4
static inline void nfsd_stats_wdeleg_getattr_inc(struct nfsd_net *nn)
static inline void nfsd_stats_cb_op_inc(struct nfsd_net *nn, u32 opcode)
{
percpu_counter_inc(&nn->counter[NFSD_STATS_WDELEG_GETATTR]);
if (opcode >= OP_CB_GETATTR && opcode <= OP_CB_OFFLOAD)
percpu_counter_inc(&nn->cb_counter[opcode]);
}
#endif
#endif /* _NFSD_STATS_H */

View File

@@ -12,12 +12,14 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprt.h>
#include <trace/misc/fs.h>
#include <trace/misc/fsnotify.h>
#include <trace/misc/nfs.h>
#include <trace/misc/sunrpc.h>
#include "export.h"
#include "nfsfh.h"
#include "xdr4.h"
#include "netns.h"
#define NFSD_TRACE_PROC_CALL_FIELDS(r) \
__field(unsigned int, netns_ino) \
@@ -271,7 +273,7 @@ TRACE_EVENT_CONDITION(nfsd_fh_verify,
TP_CONDITION(rqstp != NULL),
TP_STRUCT__entry(
__field(unsigned int, netns_ino)
__sockaddr(server, rqstp->rq_xprt->xpt_remotelen)
__sockaddr(server, rqstp->rq_xprt->xpt_locallen)
__sockaddr(client, rqstp->rq_xprt->xpt_remotelen)
__field(u32, xid)
__field(u32, fh_hash)
@@ -310,7 +312,7 @@ TRACE_EVENT_CONDITION(nfsd_fh_verify_err,
TP_CONDITION(rqstp != NULL && error),
TP_STRUCT__entry(
__field(unsigned int, netns_ino)
__sockaddr(server, rqstp->rq_xprt->xpt_remotelen)
__sockaddr(server, rqstp->rq_xprt->xpt_locallen)
__sockaddr(client, rqstp->rq_xprt->xpt_remotelen)
__field(u32, xid)
__field(u32, fh_hash)
@@ -1377,6 +1379,28 @@ TRACE_EVENT(nfsd_file_fsnotify_handle_event,
__entry->nlink, __entry->mode, __entry->mask)
);
TRACE_EVENT(nfsd_handle_dir_event,
TP_PROTO(u32 mask, const struct inode *dir, const struct qstr *name),
TP_ARGS(mask, dir, name),
TP_STRUCT__entry(
__field(u32, mask)
__field(dev_t, s_dev)
__field(u64, i_ino)
__string_len(name, name ? name->name : NULL,
name ? name->len : 0)
),
TP_fast_assign(
__entry->mask = mask;
__entry->s_dev = dir ? dir->i_sb->s_dev : 0;
__entry->i_ino = dir ? dir->i_ino : 0;
__assign_str(name);
),
TP_printk("inode=0x%x:0x%x:0x%llx mask=%s name=%s",
MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
__entry->i_ino, show_fsnotify_mask(__entry->mask),
__get_str(name))
);
DECLARE_EVENT_CLASS(nfsd_file_gc_class,
TP_PROTO(
const struct nfsd_file *nf
@@ -1677,6 +1701,7 @@ TRACE_EVENT(nfsd_cb_setup_err,
{ OP_CB_RECALL, "CB_RECALL" }, \
{ OP_CB_LAYOUTRECALL, "CB_LAYOUTRECALL" }, \
{ OP_CB_RECALL_ANY, "CB_RECALL_ANY" }, \
{ OP_CB_NOTIFY, "CB_NOTIFY" }, \
{ OP_CB_NOTIFY_LOCK, "CB_NOTIFY_LOCK" }, \
{ OP_CB_OFFLOAD, "CB_OFFLOAD" })
@@ -1727,9 +1752,10 @@ DEFINE_NFSD_CB_LIFETIME_EVENT(bc_shutdown);
TRACE_EVENT(nfsd_cb_seq_status,
TP_PROTO(
const struct rpc_task *task,
const struct nfsd4_callback *cb
const struct nfsd4_callback *cb,
const struct nfsd4_session *session
),
TP_ARGS(task, cb),
TP_ARGS(task, cb, session),
TP_STRUCT__entry(
__field(unsigned int, task_id)
__field(unsigned int, client_id)
@@ -1741,8 +1767,6 @@ TRACE_EVENT(nfsd_cb_seq_status,
__field(int, seq_status)
),
TP_fast_assign(
const struct nfs4_client *clp = cb->cb_clp;
const struct nfsd4_session *session = clp->cl_cb_session;
const struct nfsd4_sessionid *sid =
(struct nfsd4_sessionid *)&session->se_sessionid;
@@ -1768,9 +1792,10 @@ TRACE_EVENT(nfsd_cb_seq_status,
TRACE_EVENT(nfsd_cb_free_slot,
TP_PROTO(
const struct rpc_task *task,
const struct nfsd4_callback *cb
const struct nfsd4_callback *cb,
const struct nfsd4_session *session
),
TP_ARGS(task, cb),
TP_ARGS(task, cb, session),
TP_STRUCT__entry(
__field(unsigned int, task_id)
__field(unsigned int, client_id)
@@ -1781,8 +1806,6 @@ TRACE_EVENT(nfsd_cb_free_slot,
__field(u32, slot_seqno)
),
TP_fast_assign(
const struct nfs4_client *clp = cb->cb_clp;
const struct nfsd4_session *session = clp->cl_cb_session;
const struct nfsd4_sessionid *sid =
(struct nfsd4_sessionid *)&session->se_sessionid;

View File

@@ -43,6 +43,8 @@
#endif /* CONFIG_NFSD_V4 */
#include "nfsd.h"
#include "netns.h"
#include "stats.h"
#include "vfs.h"
#include "filecache.h"
#include "trace.h"
@@ -139,16 +141,17 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
err = follow_down(&path, follow_flags);
if (err < 0)
goto out;
if (path.mnt == exp->ex_path.mnt && path.dentry == dentry &&
nfsd_mountpoint(dentry, exp) == 2) {
/* This is only a mountpoint in some other namespace */
path_put(&path);
goto out;
}
exp2 = rqst_exp_get_by_name(rqstp, &path);
if (IS_ERR(exp2)) {
err = PTR_ERR(exp2);
exp2 = NULL;
/*
* We normally allow NFS clients to continue
* "underneath" a mountpoint that is not exported.
@@ -158,10 +161,7 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
*/
if (err == -ENOENT && !(exp->ex_flags & NFSEXP_V4ROOT))
err = 0;
path_put(&path);
goto out;
}
if (nfsd_v4client(rqstp) ||
} else if (nfsd_v4client(rqstp) ||
(exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) {
/* successfully crossed mount point */
/*
@@ -175,9 +175,10 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
*expp = exp2;
exp2 = exp;
}
path_put(&path);
exp_put(exp2);
out:
path_put(&path);
if (exp2)
exp_put(exp2);
return err;
}
@@ -256,7 +257,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
exp = exp_get(fhp->fh_export);
/* Lookup the name, but don't follow links */
if (isdotent(name, len)) {
if (name_is_dot_dotdot(name, len)) {
if (len==1)
dentry = dget(dparent);
else if (dparent != exp->ex_path.dentry)
@@ -419,21 +420,22 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
}
static __be32
nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct iattr *iap)
nfsd_may_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct iattr *iap)
{
struct inode *inode = d_inode(fhp->fh_dentry);
if (iap->ia_size < inode->i_size) {
__be32 err;
if (iap->ia_size >= i_size_read(inode))
return nfs_ok;
err = nfsd_permission(&rqstp->rq_cred,
fhp->fh_export, fhp->fh_dentry,
NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
if (err)
return err;
}
return nfserrno(get_write_access(inode));
return nfsd_permission(&rqstp->rq_cred, fhp->fh_export, fhp->fh_dentry,
NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
}
static __be32
nfsd_get_write_access(struct svc_fh *fhp)
{
return nfserrno(get_write_access(d_inode(fhp->fh_dentry)));
}
static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
@@ -560,12 +562,17 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
* setattr call.
*/
if (size_change) {
err = nfsd_get_write_access(rqstp, fhp, iap);
err = nfsd_get_write_access(fhp);
if (err)
return err;
}
inode_lock(inode);
if (size_change) {
err = nfsd_may_truncate(rqstp, fhp, iap);
if (err)
goto out_unlock;
}
err = fh_fill_pre_attrs(fhp);
if (err)
goto out_unlock;
@@ -1374,6 +1381,7 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file = nf->nf_file;
unsigned int nsegs, i;
ssize_t host_err;
size_t expected;
nsegs = nfsd_write_dio_iters_init(nf, rqstp->rq_bvec, nvecs,
kiocb, *cnt, segments);
@@ -1395,11 +1403,13 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
kiocb->ki_flags |= IOCB_DONTCACHE;
}
expected = iov_iter_count(&segments[i].iter);
host_err = vfs_iocb_iter_write(file, kiocb, &segments[i].iter);
if (host_err < 0)
return host_err;
*cnt += host_err;
if (host_err < segments[i].iter.count)
if (host_err < (ssize_t)expected)
break; /* partial write */
}
@@ -1876,7 +1886,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
trace_nfsd_vfs_create(rqstp, fhp, type, fname, flen);
if (isdotent(fname, flen))
if (name_is_dot_dotdot(fname, flen))
return nfserr_exist;
err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_NOP);
@@ -1978,7 +1988,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (!flen || path[0] == '\0')
goto out;
err = nfserr_exist;
if (isdotent(fname, flen))
if (name_is_dot_dotdot(fname, flen))
goto out;
err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
@@ -2055,7 +2065,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
if (!len)
goto out;
err = nfserr_exist;
if (isdotent(name, len))
if (name_is_dot_dotdot(name, len))
goto out;
err = nfs_ok;
@@ -2166,7 +2176,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
tdentry = tfhp->fh_dentry;
err = nfserr_perm;
if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
if (!flen || name_is_dot_dotdot(fname, flen) ||
!tlen || name_is_dot_dotdot(tname, tlen))
goto out;
err = nfserr_xdev;
@@ -2288,7 +2299,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
trace_nfsd_vfs_unlink(rqstp, fhp, fname, flen);
err = nfserr_acces;
if (!flen || isdotent(fname, flen))
if (!flen || name_is_dot_dotdot(fname, flen))
goto out;
err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_REMOVE);
if (err)

View File

@@ -9,7 +9,6 @@
#include <linux/fs.h>
#include <linux/posix_acl.h>
#include "nfsfh.h"
#include "nfsd.h"
/*
* Flags for nfsd_permission
@@ -45,6 +44,10 @@ struct nfsd_file;
*/
typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
struct readdir_cd {
__be32 err; /* nfs_ok, nfserr, or nfserr_eof */
};
/* nfsd/vfs.c */
struct nfsd_attrs {
struct iattr *na_iattr; /* input */

View File

@@ -7,6 +7,7 @@
#include <linux/vfs.h>
#include "nfsd.h"
#include "nfsfh.h"
#include "vfs.h"
struct nfsd_fhandle {
struct svc_fh fh;

View File

@@ -9,6 +9,7 @@
#define _LINUX_NFSD_XDR3_H
#include "xdr.h"
#include "vfs.h"
struct nfsd3_sattrargs {
struct svc_fh fh;

View File

@@ -38,7 +38,7 @@
#define _LINUX_NFSD_XDR4_H
#include "state.h"
#include "nfsd.h"
#include "vfs.h"
#define NFSD4_MAX_TAGLEN 128
#define XDR_LEN(n) (((n) + 3) & ~3)
@@ -759,28 +759,38 @@ struct nfsd4_copy {
struct nfsd42_write_res cp_res;
struct knfsd_fh fh;
/* offload callback */
struct nfsd4_cb_offload cp_cb_offload;
struct nfs4_client *cp_clp;
struct nfsd_file *nf_src;
struct nfsd_file *nf_dst;
bool attr_update;
copy_stateid_t cp_stateid;
struct list_head copies;
struct task_struct *copy_task;
refcount_t refcount;
unsigned int cp_ttl;
struct nfsd4_ssc_umount_item *ss_nsui;
struct nfs_fh c_fh;
nfs4_stateid stateid;
struct nfsd_net *cp_nn;
};
/*
* Durable state for an async (background) server-side COPY.
*
* struct nfsd4_copy is transient: it lives in the COMPOUND argument buffer
* and is reused once the op returns. An async COPY outlives the COMPOUND
* (worker kthread, reaper linkage, CB_OFFLOAD), so its params and result are
* snapshotted into the embedded cp_copy and it never points into the request
* buffer.
*/
struct nfsd4_async_copy {
struct nfs4_stid cp_stid; /* SC_TYPE_COPY, in cl_stateids */
struct nfsd4_copy cp_copy; /* operation params + result */
struct list_head copies; /* nfs4_client.async_copies */
struct task_struct *copy_task;
refcount_t refcount;
unsigned int cp_ttl;
struct nfsd4_cb_offload cp_cb_offload;
};
static inline void nfsd4_copy_set_sync(struct nfsd4_copy *copy, bool sync)
{
if (sync)
@@ -970,6 +980,11 @@ __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry,
u32 *bmval, struct svc_rqst *, int ignore_crossmnt);
u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *nne,
struct nfs4_delegation *dd, struct nfsd_file *nf,
u32 *notify_mask);
u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct nfs4_delegation *dp,
struct nfsd_file *nf);
extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, union nfsd4_op_u *u);
extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,

View File

@@ -33,6 +33,18 @@
cb_sequence_dec_sz + \
op_dec_sz)
#define NFS4_enc_cb_notify_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + enc_stateid_sz + \
enc_nfs4_fh_sz + \
1 + \
NOTIFY4_EVENT_QUEUE_SIZE * \
(2 + (NFS4_OPAQUE_LIMIT >> 2)))
#define NFS4_dec_cb_notify_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \
op_dec_sz)
#define NFS4_enc_cb_notify_lock_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
2 + 1 + \

View File

@@ -16,17 +16,23 @@ struct svc_rqst;
struct rpc_task;
struct rpc_clnt;
struct super_block;
struct module;
/*
* This is the set of functions for lockd->nfsd communication
/**
* struct nlmsvc_binding - lockd -> nfsd callback table
* @owner: module that provides this binding.
* @fopen: open a file by NFS file handle on behalf of an NLM request.
* @fclose: close a file that was previously opened via @fopen.
* Implementations MUST be semantically equivalent to fput().
*/
struct nlmsvc_binding {
struct module *owner;
int (*fopen)(struct svc_rqst *rqstp, struct nfs_fh *f,
struct file **filp, int flags);
void (*fclose)(struct file *filp);
};
extern const struct nlmsvc_binding *nlmsvc_ops;
extern const struct nlmsvc_binding __rcu *nlmsvc_ops;
/*
* Similar to nfs_client_initdata, but without the NFS-specific

View File

@@ -171,133 +171,6 @@ Needs to be updated if more operations are defined in future.*/
#define LAST_NFS42_OP OP_REMOVEXATTR
#define LAST_NFS4_OP LAST_NFS42_OP
enum nfsstat4 {
NFS4_OK = 0,
NFS4ERR_PERM = 1,
NFS4ERR_NOENT = 2,
NFS4ERR_IO = 5,
NFS4ERR_NXIO = 6,
NFS4ERR_ACCESS = 13,
NFS4ERR_EXIST = 17,
NFS4ERR_XDEV = 18,
/* Unused/reserved 19 */
NFS4ERR_NOTDIR = 20,
NFS4ERR_ISDIR = 21,
NFS4ERR_INVAL = 22,
NFS4ERR_FBIG = 27,
NFS4ERR_NOSPC = 28,
NFS4ERR_ROFS = 30,
NFS4ERR_MLINK = 31,
NFS4ERR_NAMETOOLONG = 63,
NFS4ERR_NOTEMPTY = 66,
NFS4ERR_DQUOT = 69,
NFS4ERR_STALE = 70,
NFS4ERR_BADHANDLE = 10001,
NFS4ERR_BAD_COOKIE = 10003,
NFS4ERR_NOTSUPP = 10004,
NFS4ERR_TOOSMALL = 10005,
NFS4ERR_SERVERFAULT = 10006,
NFS4ERR_BADTYPE = 10007,
NFS4ERR_DELAY = 10008,
NFS4ERR_SAME = 10009,
NFS4ERR_DENIED = 10010,
NFS4ERR_EXPIRED = 10011,
NFS4ERR_LOCKED = 10012,
NFS4ERR_GRACE = 10013,
NFS4ERR_FHEXPIRED = 10014,
NFS4ERR_SHARE_DENIED = 10015,
NFS4ERR_WRONGSEC = 10016,
NFS4ERR_CLID_INUSE = 10017,
NFS4ERR_RESOURCE = 10018,
NFS4ERR_MOVED = 10019,
NFS4ERR_NOFILEHANDLE = 10020,
NFS4ERR_MINOR_VERS_MISMATCH = 10021,
NFS4ERR_STALE_CLIENTID = 10022,
NFS4ERR_STALE_STATEID = 10023,
NFS4ERR_OLD_STATEID = 10024,
NFS4ERR_BAD_STATEID = 10025,
NFS4ERR_BAD_SEQID = 10026,
NFS4ERR_NOT_SAME = 10027,
NFS4ERR_LOCK_RANGE = 10028,
NFS4ERR_SYMLINK = 10029,
NFS4ERR_RESTOREFH = 10030,
NFS4ERR_LEASE_MOVED = 10031,
NFS4ERR_ATTRNOTSUPP = 10032,
NFS4ERR_NO_GRACE = 10033,
NFS4ERR_RECLAIM_BAD = 10034,
NFS4ERR_RECLAIM_CONFLICT = 10035,
NFS4ERR_BADXDR = 10036,
NFS4ERR_LOCKS_HELD = 10037,
NFS4ERR_OPENMODE = 10038,
NFS4ERR_BADOWNER = 10039,
NFS4ERR_BADCHAR = 10040,
NFS4ERR_BADNAME = 10041,
NFS4ERR_BAD_RANGE = 10042,
NFS4ERR_LOCK_NOTSUPP = 10043,
NFS4ERR_OP_ILLEGAL = 10044,
NFS4ERR_DEADLOCK = 10045,
NFS4ERR_FILE_OPEN = 10046,
NFS4ERR_ADMIN_REVOKED = 10047,
NFS4ERR_CB_PATH_DOWN = 10048,
/* nfs41 */
NFS4ERR_BADIOMODE = 10049,
NFS4ERR_BADLAYOUT = 10050,
NFS4ERR_BAD_SESSION_DIGEST = 10051,
NFS4ERR_BADSESSION = 10052,
NFS4ERR_BADSLOT = 10053,
NFS4ERR_COMPLETE_ALREADY = 10054,
NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
NFS4ERR_DELEG_ALREADY_WANTED = 10056,
NFS4ERR_BACK_CHAN_BUSY = 10057, /* backchan reqs outstanding */
NFS4ERR_LAYOUTTRYLATER = 10058,
NFS4ERR_LAYOUTUNAVAILABLE = 10059,
NFS4ERR_NOMATCHING_LAYOUT = 10060,
NFS4ERR_RECALLCONFLICT = 10061,
NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
NFS4ERR_SEQ_MISORDERED = 10063, /* unexpected seq.id in req */
NFS4ERR_SEQUENCE_POS = 10064, /* [CB_]SEQ. op not 1st op */
NFS4ERR_REQ_TOO_BIG = 10065, /* request too big */
NFS4ERR_REP_TOO_BIG = 10066, /* reply too big */
NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067, /* rep. not all cached */
NFS4ERR_RETRY_UNCACHED_REP = 10068, /* retry & rep. uncached */
NFS4ERR_UNSAFE_COMPOUND = 10069, /* retry/recovery too hard */
NFS4ERR_TOO_MANY_OPS = 10070, /* too many ops in [CB_]COMP */
NFS4ERR_OP_NOT_IN_SESSION = 10071, /* op needs [CB_]SEQ. op */
NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
/* Error 10073 is unused. */
NFS4ERR_CLIENTID_BUSY = 10074, /* clientid has state */
NFS4ERR_PNFS_IO_HOLE = 10075, /* IO to _SPARSE file hole */
NFS4ERR_SEQ_FALSE_RETRY = 10076, /* retry not original */
NFS4ERR_BAD_HIGH_SLOT = 10077, /* sequence arg bad */
NFS4ERR_DEADSESSION = 10078, /* persistent session dead */
NFS4ERR_ENCR_ALG_UNSUPP = 10079, /* SSV alg mismatch */
NFS4ERR_PNFS_NO_LAYOUT = 10080, /* direct I/O with no layout */
NFS4ERR_NOT_ONLY_OP = 10081, /* bad compound */
NFS4ERR_WRONG_CRED = 10082, /* permissions:state change */
NFS4ERR_WRONG_TYPE = 10083, /* current operation mismatch */
NFS4ERR_DIRDELEG_UNAVAIL = 10084, /* no directory delegation */
NFS4ERR_REJECT_DELEG = 10085, /* on callback */
NFS4ERR_RETURNCONFLICT = 10086, /* outstanding layoutreturn */
NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */
/* nfs42 */
NFS4ERR_PARTNER_NOTSUPP = 10088,
NFS4ERR_PARTNER_NO_AUTH = 10089,
NFS4ERR_UNION_NOTSUPP = 10090,
NFS4ERR_OFFLOAD_DENIED = 10091,
NFS4ERR_WRONG_LFS = 10092,
NFS4ERR_BADLABEL = 10093,
NFS4ERR_OFFLOAD_NO_REQS = 10094,
/* xattr (RFC8276) */
NFS4ERR_NOXATTR = 10095,
NFS4ERR_XATTR2BIG = 10096,
/* can be used for internal errors */
NFS4ERR_FIRST_FREE
};
/* error codes for internal client use */
#define NFS4ERR_RESET_TO_MDS 12001
#define NFS4ERR_RESET_TO_PNFS 12002

View File

@@ -32,6 +32,7 @@ int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs);
void xprt_destroy_bc(struct rpc_xprt *xprt, unsigned int max_reqs);
void xprt_free_bc_rqst(struct rpc_rqst *req);
unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt);
void xprt_svc_shutdown_bc(struct rpc_xprt *xprt);
void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv);
/*
@@ -71,6 +72,10 @@ static inline void xprt_free_bc_request(struct rpc_rqst *req)
{
}
static inline void xprt_svc_shutdown_bc(struct rpc_xprt *xprt)
{
}
static inline void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv)
{
svc_destroy(serv);

View File

@@ -37,9 +37,15 @@ struct svc_stat {
rpcbadfmt,
rpcbadauth,
rpcbadclnt;
/* Per-version per-procedure call counts (per-cpu, per-netns) */
unsigned long __percpu **vs_count;
};
struct net;
int svc_stat_alloc_counts(struct svc_stat *statp);
void svc_stat_free_counts(struct svc_stat *statp);
#ifdef CONFIG_PROC_FS
int rpc_proc_init(struct net *);
void rpc_proc_exit(struct net *);

View File

@@ -85,7 +85,6 @@ struct svc_serv {
char * sv_name; /* service name */
unsigned int sv_nrpools; /* number of thread pools */
bool sv_is_pooled; /* is this a pooled service? */
struct svc_pool * sv_pools; /* array of thread pools */
int (*sv_threadfn)(void *data);
@@ -408,7 +407,6 @@ struct svc_version {
u32 vs_vers; /* version number */
u32 vs_nproc; /* number of procedures */
const struct svc_procedure *vs_proc; /* per-procedure info */
unsigned long __percpu *vs_count; /* call counts */
u32 vs_xdrsize; /* xdrsize needed for this version */
/* Don't register with rpcbind */
@@ -469,6 +467,7 @@ int svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool,
unsigned int min_threads, unsigned int max_threads);
int svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
unsigned int nrservs);
unsigned int svc_serv_maxthreads(const struct svc_serv *serv);
int svc_pool_stats_open(struct svc_info *si, struct file *file);
void svc_process(struct svc_rqst *rqstp);
void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp);
@@ -479,6 +478,7 @@ void svc_wake_up(struct svc_serv *);
void svc_reserve(struct svc_rqst *rqstp, int space);
void svc_pool_wake_idle_thread(struct svc_pool *pool);
struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv);
unsigned int svc_serv_nrpools(const struct svc_serv *serv);
char * svc_print_addr(struct svc_rqst *, char *, size_t);
const char * svc_proc_name(const struct svc_rqst *rqstp);
int svc_encode_result_payload(struct svc_rqst *rqstp,

View File

@@ -97,7 +97,7 @@ pcl_next_chunk(const struct svc_rdma_pcl *pcl, struct svc_rdma_chunk *chunk)
*/
#define pcl_for_each_segment(pos, chunk) \
for (pos = &(chunk)->ch_segments[0]; \
pos <= &(chunk)->ch_segments[(chunk)->ch_segcount - 1]; \
pos < &(chunk)->ch_segments[(chunk)->ch_segcount]; \
pos++)
/**
@@ -119,6 +119,8 @@ extern bool pcl_alloc_call(struct svc_rdma_recv_ctxt *rctxt, __be32 *p);
extern bool pcl_alloc_read(struct svc_rdma_recv_ctxt *rctxt, __be32 *p);
extern bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt,
struct svc_rdma_pcl *pcl, __be32 *p);
extern bool pcl_check_read_chunk_positions(struct svc_rdma_recv_ctxt *rctxt,
unsigned int inline_len);
extern int pcl_process_nonpayloads(const struct svc_rdma_pcl *pcl,
const struct xdr_buf *xdr,
int (*actor)(const struct xdr_buf *,

View File

@@ -296,4 +296,36 @@ xdrgen_encode_opaque(struct xdr_stream *xdr, opaque val)
return true;
}
struct svc_rqst;
/**
* xdrgen_svc_decode_void - Decode a void argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
static inline bool
xdrgen_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_decode_void(xdr);
}
/**
* xdrgen_svc_encode_void - Encode a void result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
static inline bool
xdrgen_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_encode_void(xdr);
}
#endif /* _SUNRPC_XDRGEN__BUILTINS_H_ */

View File

@@ -25,6 +25,8 @@ typedef struct {
#define XDR_void (0)
#define XDR_bool (1)
#define XDR_short (1)
#define XDR_unsigned_short (1)
#define XDR_int (1)
#define XDR_unsigned_int (1)
#define XDR_long (1)

View File

@@ -1,7 +1,7 @@
/* 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: Thu Jan 8 23:12:07 2026 */
/* XDR specification modification time: Tue Jun 30 11:57:21 2026 */
#ifndef _LINUX_XDRGEN_NFS4_1_DEF_H
#define _LINUX_XDRGEN_NFS4_1_DEF_H
@@ -9,15 +9,149 @@
#include <linux/types.h>
#include <linux/sunrpc/xdrgen/_defs.h>
typedef s64 int64_t;
typedef s32 int32_t;
typedef u32 uint32_t;
typedef s64 int64_t;
typedef u64 uint64_t;
enum { NFS4_VERIFIER_SIZE = 8 };
enum { NFS4_FHSIZE = 128 };
enum nfsstat4 {
NFS4_OK = 0,
NFS4ERR_PERM = 1,
NFS4ERR_NOENT = 2,
NFS4ERR_IO = 5,
NFS4ERR_NXIO = 6,
NFS4ERR_ACCESS = 13,
NFS4ERR_EXIST = 17,
NFS4ERR_XDEV = 18,
NFS4ERR_NOTDIR = 20,
NFS4ERR_ISDIR = 21,
NFS4ERR_INVAL = 22,
NFS4ERR_FBIG = 27,
NFS4ERR_NOSPC = 28,
NFS4ERR_ROFS = 30,
NFS4ERR_MLINK = 31,
NFS4ERR_NAMETOOLONG = 63,
NFS4ERR_NOTEMPTY = 66,
NFS4ERR_DQUOT = 69,
NFS4ERR_STALE = 70,
NFS4ERR_BADHANDLE = 10001,
NFS4ERR_BAD_COOKIE = 10003,
NFS4ERR_NOTSUPP = 10004,
NFS4ERR_TOOSMALL = 10005,
NFS4ERR_SERVERFAULT = 10006,
NFS4ERR_BADTYPE = 10007,
NFS4ERR_DELAY = 10008,
NFS4ERR_SAME = 10009,
NFS4ERR_DENIED = 10010,
NFS4ERR_EXPIRED = 10011,
NFS4ERR_LOCKED = 10012,
NFS4ERR_GRACE = 10013,
NFS4ERR_FHEXPIRED = 10014,
NFS4ERR_SHARE_DENIED = 10015,
NFS4ERR_WRONGSEC = 10016,
NFS4ERR_CLID_INUSE = 10017,
NFS4ERR_RESOURCE = 10018,
NFS4ERR_MOVED = 10019,
NFS4ERR_NOFILEHANDLE = 10020,
NFS4ERR_MINOR_VERS_MISMATCH = 10021,
NFS4ERR_STALE_CLIENTID = 10022,
NFS4ERR_STALE_STATEID = 10023,
NFS4ERR_OLD_STATEID = 10024,
NFS4ERR_BAD_STATEID = 10025,
NFS4ERR_BAD_SEQID = 10026,
NFS4ERR_NOT_SAME = 10027,
NFS4ERR_LOCK_RANGE = 10028,
NFS4ERR_SYMLINK = 10029,
NFS4ERR_RESTOREFH = 10030,
NFS4ERR_LEASE_MOVED = 10031,
NFS4ERR_ATTRNOTSUPP = 10032,
NFS4ERR_NO_GRACE = 10033,
NFS4ERR_RECLAIM_BAD = 10034,
NFS4ERR_RECLAIM_CONFLICT = 10035,
NFS4ERR_BADXDR = 10036,
NFS4ERR_LOCKS_HELD = 10037,
NFS4ERR_OPENMODE = 10038,
NFS4ERR_BADOWNER = 10039,
NFS4ERR_BADCHAR = 10040,
NFS4ERR_BADNAME = 10041,
NFS4ERR_BAD_RANGE = 10042,
NFS4ERR_LOCK_NOTSUPP = 10043,
NFS4ERR_OP_ILLEGAL = 10044,
NFS4ERR_DEADLOCK = 10045,
NFS4ERR_FILE_OPEN = 10046,
NFS4ERR_ADMIN_REVOKED = 10047,
NFS4ERR_CB_PATH_DOWN = 10048,
NFS4ERR_BADIOMODE = 10049,
NFS4ERR_BADLAYOUT = 10050,
NFS4ERR_BAD_SESSION_DIGEST = 10051,
NFS4ERR_BADSESSION = 10052,
NFS4ERR_BADSLOT = 10053,
NFS4ERR_COMPLETE_ALREADY = 10054,
NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
NFS4ERR_DELEG_ALREADY_WANTED = 10056,
NFS4ERR_BACK_CHAN_BUSY = 10057,
NFS4ERR_LAYOUTTRYLATER = 10058,
NFS4ERR_LAYOUTUNAVAILABLE = 10059,
NFS4ERR_NOMATCHING_LAYOUT = 10060,
NFS4ERR_RECALLCONFLICT = 10061,
NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
NFS4ERR_SEQ_MISORDERED = 10063,
NFS4ERR_SEQUENCE_POS = 10064,
NFS4ERR_REQ_TOO_BIG = 10065,
NFS4ERR_REP_TOO_BIG = 10066,
NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067,
NFS4ERR_RETRY_UNCACHED_REP = 10068,
NFS4ERR_UNSAFE_COMPOUND = 10069,
NFS4ERR_TOO_MANY_OPS = 10070,
NFS4ERR_OP_NOT_IN_SESSION = 10071,
NFS4ERR_HASH_ALG_UNSUPP = 10072,
NFS4ERR_CLIENTID_BUSY = 10074,
NFS4ERR_PNFS_IO_HOLE = 10075,
NFS4ERR_SEQ_FALSE_RETRY = 10076,
NFS4ERR_BAD_HIGH_SLOT = 10077,
NFS4ERR_DEADSESSION = 10078,
NFS4ERR_ENCR_ALG_UNSUPP = 10079,
NFS4ERR_PNFS_NO_LAYOUT = 10080,
NFS4ERR_NOT_ONLY_OP = 10081,
NFS4ERR_WRONG_CRED = 10082,
NFS4ERR_WRONG_TYPE = 10083,
NFS4ERR_DIRDELEG_UNAVAIL = 10084,
NFS4ERR_REJECT_DELEG = 10085,
NFS4ERR_RETURNCONFLICT = 10086,
NFS4ERR_DELEG_REVOKED = 10087,
NFS4ERR_PARTNER_NOTSUPP = 10088,
NFS4ERR_PARTNER_NO_AUTH = 10089,
NFS4ERR_UNION_NOTSUPP = 10090,
NFS4ERR_OFFLOAD_DENIED = 10091,
NFS4ERR_WRONG_LFS = 10092,
NFS4ERR_BADLABEL = 10093,
NFS4ERR_OFFLOAD_NO_REQS = 10094,
NFS4ERR_NOXATTR = 10095,
NFS4ERR_XATTR2BIG = 10096,
};
typedef enum nfsstat4 nfsstat4;
typedef opaque attrlist4;
typedef struct {
u32 count;
uint32_t *element;
} bitmap4;
typedef u8 verifier4[NFS4_VERIFIER_SIZE];
typedef uint64_t nfs_cookie4;
typedef opaque nfs_fh4;
typedef opaque utf8string;
typedef utf8string utf8str_cis;
@@ -26,11 +160,30 @@ typedef utf8string utf8str_cs;
typedef utf8string utf8str_mixed;
typedef utf8str_cs component4;
typedef utf8str_cs linktext4;
typedef struct {
u32 count;
component4 *element;
} pathname4;
struct nfstime4 {
int64_t seconds;
uint32_t nseconds;
};
struct fattr4 {
bitmap4 attrmask;
attrlist4 attr_vals;
};
struct stateid4 {
uint32_t seqid;
u8 other[12];
};
typedef bool fattr4_offline;
enum { FATTR4_OFFLINE = 83 };
@@ -216,11 +369,109 @@ enum { FATTR4_POSIX_DEFAULT_ACL = 91 };
enum { FATTR4_POSIX_ACCESS_ACL = 92 };
#define NFS4_int64_t_sz \
(XDR_hyper)
enum notify_type4 {
NOTIFY4_CHANGE_CHILD_ATTRS = 0,
NOTIFY4_CHANGE_DIR_ATTRS = 1,
NOTIFY4_REMOVE_ENTRY = 2,
NOTIFY4_ADD_ENTRY = 3,
NOTIFY4_RENAME_ENTRY = 4,
NOTIFY4_CHANGE_COOKIE_VERIFIER = 5,
NOTIFY4_GFLAG_EXTEND = 6,
NOTIFY4_AUFLAG_VALID = 7,
NOTIFY4_AUFLAG_USER = 8,
NOTIFY4_AUFLAG_GROUP = 9,
NOTIFY4_AUFLAG_OTHER = 10,
NOTIFY4_CHANGE_AUTH = 11,
NOTIFY4_CFLAG_ORDER = 12,
NOTIFY4_AUFLAG_GANOW = 13,
NOTIFY4_AUFLAG_GALATER = 14,
NOTIFY4_CHANGE_GA = 15,
NOTIFY4_CHANGE_AMASK = 16,
};
typedef enum notify_type4 notify_type4;
struct notify_entry4 {
component4 ne_file;
struct fattr4 ne_attrs;
};
struct prev_entry4 {
struct notify_entry4 pe_prev_entry;
nfs_cookie4 pe_prev_entry_cookie;
};
struct notify_remove4 {
struct notify_entry4 nrm_old_entry;
nfs_cookie4 nrm_old_entry_cookie;
};
struct notify_add4 {
struct {
u32 count;
struct notify_remove4 *element;
} nad_old_entry;
struct notify_entry4 nad_new_entry;
struct {
u32 count;
nfs_cookie4 *element;
} nad_new_entry_cookie;
struct {
u32 count;
struct prev_entry4 *element;
} nad_prev_entry;
bool nad_last_entry;
};
struct notify_attr4 {
struct notify_entry4 na_changed_entry;
};
struct notify_rename4 {
struct notify_remove4 nrn_old_entry;
struct notify_add4 nrn_new_entry;
};
struct notify_verifier4 {
verifier4 nv_old_cookieverf;
verifier4 nv_new_cookieverf;
};
typedef opaque notifylist4;
struct notify4 {
bitmap4 notify_mask;
notifylist4 notify_vals;
};
struct CB_NOTIFY4args {
struct stateid4 cna_stateid;
nfs_fh4 cna_fh;
struct {
u32 count;
struct notify4 *element;
} cna_changes;
};
struct CB_NOTIFY4res {
nfsstat4 cnr_status;
};
#define NFS4_int32_t_sz \
(XDR_int)
#define NFS4_uint32_t_sz \
(XDR_unsigned_int)
#define NFS4_int64_t_sz \
(XDR_hyper)
#define NFS4_uint64_t_sz \
(XDR_unsigned_hyper)
#define NFS4_nfsstat4_sz (XDR_int)
#define NFS4_attrlist4_sz (XDR_unsigned_int)
#define NFS4_bitmap4_sz (XDR_unsigned_int)
#define NFS4_verifier4_sz (XDR_QUADLEN(NFS4_VERIFIER_SIZE))
#define NFS4_nfs_cookie4_sz \
(NFS4_uint64_t_sz)
#define NFS4_nfs_fh4_sz (XDR_unsigned_int + XDR_QUADLEN(NFS4_FHSIZE))
#define NFS4_utf8string_sz (XDR_unsigned_int)
#define NFS4_utf8str_cis_sz \
(NFS4_utf8string_sz)
@@ -228,8 +479,17 @@ enum { FATTR4_POSIX_ACCESS_ACL = 92 };
(NFS4_utf8string_sz)
#define NFS4_utf8str_mixed_sz \
(NFS4_utf8string_sz)
#define NFS4_component4_sz \
(NFS4_utf8str_cs_sz)
#define NFS4_linktext4_sz \
(NFS4_utf8str_cs_sz)
#define NFS4_pathname4_sz (XDR_unsigned_int)
#define NFS4_nfstime4_sz \
(NFS4_int64_t_sz + NFS4_uint32_t_sz)
#define NFS4_fattr4_sz \
(NFS4_bitmap4_sz + NFS4_attrlist4_sz)
#define NFS4_stateid4_sz \
(NFS4_uint32_t_sz + XDR_QUADLEN(12))
#define NFS4_fattr4_offline_sz \
(XDR_bool)
#define NFS4_open_arguments4_sz \
@@ -259,5 +519,27 @@ enum { FATTR4_POSIX_ACCESS_ACL = 92 };
(NFS4_aclscope4_sz)
#define NFS4_fattr4_posix_default_acl_sz (XDR_unsigned_int)
#define NFS4_fattr4_posix_access_acl_sz (XDR_unsigned_int)
#define NFS4_notify_type4_sz (XDR_int)
#define NFS4_notify_entry4_sz \
(NFS4_component4_sz + NFS4_fattr4_sz)
#define NFS4_prev_entry4_sz \
(NFS4_notify_entry4_sz + NFS4_nfs_cookie4_sz)
#define NFS4_notify_remove4_sz \
(NFS4_notify_entry4_sz + NFS4_nfs_cookie4_sz)
#define NFS4_notify_add4_sz \
(XDR_unsigned_int + (1 * (NFS4_notify_remove4_sz)) + NFS4_notify_entry4_sz + XDR_unsigned_int + (1 * (NFS4_nfs_cookie4_sz)) + XDR_unsigned_int + (1 * (NFS4_prev_entry4_sz)) + XDR_bool)
#define NFS4_notify_attr4_sz \
(NFS4_notify_entry4_sz)
#define NFS4_notify_rename4_sz \
(NFS4_notify_remove4_sz + NFS4_notify_add4_sz)
#define NFS4_notify_verifier4_sz \
(NFS4_verifier4_sz + NFS4_verifier4_sz)
#define NFS4_notifylist4_sz (XDR_unsigned_int)
#define NFS4_notify4_sz \
(NFS4_bitmap4_sz + NFS4_notifylist4_sz)
#define NFS4_CB_NOTIFY4args_sz \
(NFS4_stateid4_sz + NFS4_nfs_fh4_sz + XDR_unsigned_int)
#define NFS4_CB_NOTIFY4res_sz \
(NFS4_nfsstat4_sz)
#endif /* _LINUX_XDRGEN_NFS4_1_DEF_H */

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x */
/* XDR specification modification time: Thu Apr 23 10:56:34 2026 */
/* XDR specification modification time: Mon Jun 29 20:42:29 2026 */
#ifndef _LINUX_XDRGEN_NLM3_DEF_H
#define _LINUX_XDRGEN_NLM3_DEF_H

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x */
/* XDR specification modification time: Thu Dec 25 13:10:19 2025 */
/* XDR specification modification time: Mon Jun 29 20:42:29 2026 */
#ifndef _LINUX_XDRGEN_NLM4_DEF_H
#define _LINUX_XDRGEN_NLM4_DEF_H

View File

@@ -17,11 +17,9 @@
#include <linux/types.h>
#define NFS4_BITMAP_SIZE 3
#define NFS4_VERIFIER_SIZE 8
#define NFS4_STATEID_SEQID_SIZE 4
#define NFS4_STATEID_OTHER_SIZE 12
#define NFS4_STATEID_SIZE (NFS4_STATEID_SEQID_SIZE + NFS4_STATEID_OTHER_SIZE)
#define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_OPAQUE_LIMIT 1024

View File

@@ -225,6 +225,41 @@ enum {
NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1)
};
enum {
NFSD_A_SERVER_PROC_ENTRY_OP = 1,
NFSD_A_SERVER_PROC_ENTRY_COUNT,
NFSD_A_SERVER_PROC_ENTRY_PAD,
__NFSD_A_SERVER_PROC_ENTRY_MAX,
NFSD_A_SERVER_PROC_ENTRY_MAX = (__NFSD_A_SERVER_PROC_ENTRY_MAX - 1)
};
enum {
NFSD_A_SERVER_STATS_RC_HITS = 1,
NFSD_A_SERVER_STATS_RC_MISSES,
NFSD_A_SERVER_STATS_RC_NOCACHE,
NFSD_A_SERVER_STATS_PAD,
NFSD_A_SERVER_STATS_FH_STALE,
NFSD_A_SERVER_STATS_IO_READ,
NFSD_A_SERVER_STATS_IO_WRITE,
NFSD_A_SERVER_STATS_NETCNT,
NFSD_A_SERVER_STATS_NETUDPCNT,
NFSD_A_SERVER_STATS_NETTCPCNT,
NFSD_A_SERVER_STATS_NETTCPCONN,
NFSD_A_SERVER_STATS_RPCCNT,
NFSD_A_SERVER_STATS_RPCBADFMT,
NFSD_A_SERVER_STATS_RPCBADAUTH,
NFSD_A_SERVER_STATS_RPCBADCLNT,
NFSD_A_SERVER_STATS_PROC2_OPS,
NFSD_A_SERVER_STATS_PROC3_OPS,
NFSD_A_SERVER_STATS_PROC4_OPS,
NFSD_A_SERVER_STATS_PROC4OPS_OPS,
NFSD_A_SERVER_STATS_PROC4CB_OPS,
__NFSD_A_SERVER_STATS_MAX,
NFSD_A_SERVER_STATS_MAX = (__NFSD_A_SERVER_STATS_MAX - 1)
};
enum {
NFSD_CMD_RPC_STATUS_GET = 1,
NFSD_CMD_THREADS_SET,
@@ -244,6 +279,7 @@ enum {
NFSD_CMD_UNLOCK_IP,
NFSD_CMD_UNLOCK_FILESYSTEM,
NFSD_CMD_UNLOCK_EXPORT,
NFSD_CMD_SERVER_STATS_GET,
__NFSD_CMD_MAX,
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)

View File

@@ -2072,7 +2072,11 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
goto unwrap_failed;
opaque_len = be32_to_cpup(p++);
offset = (u8 *)(p) - (u8 *)head->iov_base;
if (offset + opaque_len > rcv_buf->len)
if (offset > rcv_buf->len)
goto unwrap_failed;
if (opaque_len > rcv_buf->len - offset)
goto unwrap_failed;
if (opaque_len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;
maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,

View File

@@ -89,6 +89,9 @@ gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
dprintk("RPC: %s\n", __func__);
if (read_token->len < GSS_KRB5_TOK_HDR_LEN + cksum_len)
return GSS_S_DEFECTIVE_TOKEN;
memcpy(&be16_ptr, (char *) ptr, 2);
if (be16_to_cpu(be16_ptr) != KG2_TOK_MIC)
return GSS_S_DEFECTIVE_TOKEN;

View File

@@ -73,6 +73,8 @@ static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
int shifted = 0;
int this_shift;
if (!buf->len)
return;
shift %= buf->len;
while (shifted < shift) {
this_shift = min(shift - shifted, LOCAL_BUF_LEN);
@@ -85,6 +87,8 @@ static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
{
struct xdr_buf subbuf;
if (buf->len <= base)
return;
xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
_rotate_left(&subbuf, shift);
}
@@ -154,6 +158,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
dprintk("RPC: %s\n", __func__);
if (len - offset <= GSS_KRB5_TOK_HDR_LEN)
return GSS_S_DEFECTIVE_TOKEN;
ptr = buf->head[0].iov_base + offset;
if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
@@ -220,14 +227,16 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
* head buffer space rather than that actually occupied.
*/
movelen = min_t(unsigned int, buf->head[0].iov_len, len);
if (movelen < offset + GSS_KRB5_TOK_HDR_LEN + headskip)
return GSS_S_DEFECTIVE_TOKEN;
movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
buf->head[0].iov_len);
memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
/* Trim off the trailing "extra count" and checksum blob */
if (ec + GSS_KRB5_TOK_HDR_LEN + tailskip > buf->len - offset)
return GSS_S_DEFECTIVE_TOKEN;
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
*align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip);

View File

@@ -121,12 +121,6 @@ static int gssp_rpc_create(struct net *net, struct rpc_clnt **_clnt)
return result;
}
void init_gssp_clnt(struct sunrpc_net *sn)
{
mutex_init(&sn->gssp_lock);
sn->gssp_clnt = NULL;
}
int set_gssp_clnt(struct net *net)
{
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);

View File

@@ -29,7 +29,6 @@ int gssp_accept_sec_context_upcall(struct net *net,
struct gssp_upcall_data *data);
void gssp_free_upcall_data(struct gssp_upcall_data *data);
void init_gssp_clnt(struct sunrpc_net *);
int set_gssp_clnt(struct net *);
void clear_gssp_clnt(struct sunrpc_net *);

View File

@@ -222,7 +222,8 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr,
return 0;
out_free_groups:
groups_free(creds->cr_group_info);
put_group_info(creds->cr_group_info);
creds->cr_group_info = NULL;
return err;
}
@@ -230,6 +231,7 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
struct gssx_option_array *oa)
{
struct svc_cred *creds;
bool creds_decoded = false;
u32 count, i;
__be32 *p;
int err;
@@ -242,12 +244,12 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
return 0;
/* we recognize only 1 currently: CREDS_VALUE */
oa->count = 1;
oa->data = kmalloc_obj(struct gssx_option);
if (!oa->data)
return -ENOMEM;
oa->count = 1;
creds = kzalloc_obj(struct svc_cred);
if (!creds) {
err = -ENOMEM;
@@ -280,9 +282,14 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
if (length == sizeof(CREDS_VALUE) &&
memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
/* We have creds here. parse them */
if (creds_decoded) {
err = -EINVAL;
goto free_creds;
}
err = gssx_dec_linux_creds(xdr, creds);
if (err)
goto free_creds;
creds_decoded = true;
oa->data[0].value.len = 1; /* presence */
} else {
/* consume uninteresting buffer */
@@ -294,8 +301,10 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
return 0;
free_creds:
free_svc_cred(creds);
kfree(creds);
free_oa:
oa->count = 0;
kfree(oa->data);
oa->data = NULL;
return err;

View File

@@ -949,6 +949,8 @@ svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx)
}
if (len > xdr_stream_remaining(xdr))
goto unwrap_failed;
if (len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;
offset = xdr_stream_pos(xdr);
saved_len = buf->len;
@@ -1466,7 +1468,6 @@ static int create_use_gss_proxy_proc_entry(struct net *net)
&use_gss_proxy_proc_ops, net);
if (!*p)
return -ENOMEM;
init_gssp_clnt(sn);
return 0;
}
@@ -1573,6 +1574,9 @@ svcauth_gss_decode_credbody(struct xdr_stream *xdr,
u32 body_len;
__be32 *p;
/* Early-return paths leave deterministic state, not stale residue. */
memset(gc, 0, sizeof(*gc));
p = xdr_inline_decode(xdr, XDR_UNIT);
if (!p)
return false;
@@ -1942,6 +1946,8 @@ svcauth_gss_release(struct svc_rqst *rqstp)
if (!gsd)
goto out;
if (rqstp->rq_auth_stat != rpc_auth_ok)
goto out;
gc = &gsd->clcred;
if (gc->gc_proc != RPC_GSS_PROC_DATA)
goto out;

View File

@@ -25,20 +25,39 @@ unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt)
}
/*
* Helper function to nullify backchannel server pointer in transport.
* We need to synchronize setting the pointer to NULL (done so after
* the backchannel server is shutdown) with the usage of that pointer
* by the backchannel request processing routines
* xprt_complete_bc_request() and rpcrdma_bc_receive_call().
* Close the backchannel producer side, drain any requests still
* queued on sv_cb_list, then destroy the callback service.
*/
void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv)
{
spin_lock(&xprt->bc_pa_lock);
struct svc_serv *bc_serv = *serv;
struct rpc_rqst *req;
xprt_svc_shutdown_bc(xprt);
while ((req = lwq_dequeue(&bc_serv->sv_cb_list, struct rpc_rqst,
rq_bc_list)) != NULL) {
atomic_dec(&req->rq_xprt->bc_slot_count);
xprt_free_bc_request(req);
}
svc_destroy(serv);
}
EXPORT_SYMBOL_GPL(xprt_svc_destroy_nullify_bc);
/*
* Clear the backchannel server pointer in the transport. The NULL
* store is serialized under bc_pa_lock against readers of
* xprt->bc_serv in xprt_complete_bc_request() and
* rpcrdma_bc_receive_call(). Clearing it before the callback service
* is stopped prevents a producer from enqueueing onto a service that
* is being torn down.
*/
void xprt_svc_shutdown_bc(struct rpc_xprt *xprt)
{
spin_lock(&xprt->bc_pa_lock);
xprt->bc_serv = NULL;
spin_unlock(&xprt->bc_pa_lock);
}
EXPORT_SYMBOL_GPL(xprt_svc_destroy_nullify_bc);
EXPORT_SYMBOL_GPL(xprt_svc_shutdown_bc);
/*
* Helper routines that track the number of preallocation elements
@@ -393,7 +412,12 @@ void xprt_enqueue_bc_request(struct rpc_rqst *req)
if (bc_serv) {
lwq_enqueue(&req->rq_bc_list, &bc_serv->sv_cb_list);
svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
spin_unlock(&xprt->bc_pa_lock);
return;
}
spin_unlock(&xprt->bc_pa_lock);
atomic_dec(&xprt->bc_slot_count);
xprt_free_bc_request(req);
}
EXPORT_SYMBOL_GPL(xprt_enqueue_bc_request);

View File

@@ -430,10 +430,9 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd)
list_del_init(&cd->others);
spin_unlock(&cd->hash_lock);
spin_unlock(&cache_list_lock);
if (list_empty(&cache_list)) {
/* module must be being unloaded so its safe to kill the worker */
cancel_delayed_work_sync(&cache_cleaner);
}
cancel_delayed_work_sync(&cache_cleaner);
if (!list_empty(&cache_list))
queue_delayed_work(system_power_efficient_wq, &cache_cleaner, 0);
}
EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail);

View File

@@ -108,7 +108,7 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp)
for (j = 0; j < vers->vs_nproc; j++) {
count = 0;
for_each_possible_cpu(k)
count += per_cpu(vers->vs_count[j], k);
count += per_cpu(statp->vs_count[i][j], k);
seq_printf(seq, " %lu", count);
}
seq_putc(seq, '\n');

View File

@@ -57,6 +57,7 @@ static __net_init int sunrpc_init_net(struct net *net)
INIT_LIST_HEAD(&sn->all_clients);
spin_lock_init(&sn->rpc_client_lock);
spin_lock_init(&sn->rpcb_clnt_lock);
mutex_init(&sn->gssp_lock);
return 0;
err_pipefs:

View File

@@ -38,82 +38,36 @@
static void svc_unregister(const struct svc_serv *serv, struct net *net);
#define SVC_POOL_DEFAULT SVC_POOL_GLOBAL
/*
* Mode for mapping cpus to pools.
*/
enum {
SVC_POOL_AUTO = -1, /* choose one of the others */
SVC_POOL_GLOBAL, /* no mapping, just a single global pool
* (legacy & UP mode) */
SVC_POOL_PERCPU, /* one pool per cpu */
SVC_POOL_PERNODE /* one pool per numa node */
};
/*
* Structure for mapping cpus to pools and vice versa.
* Structure for mapping nodes to pools and vice versa.
* Setup once during sunrpc initialisation.
*/
struct svc_pool_map {
int count; /* How many svc_servs use us */
int mode; /* Note: int not enum to avoid
* warnings about "enumeration value
* not handled in switch" */
unsigned int npools;
unsigned int *pool_to; /* maps pool id to cpu or node */
unsigned int *to_pool; /* maps cpu or node to pool id */
unsigned int *pool_to; /* maps pool id to node */
unsigned int *to_pool; /* maps node to pool id */
};
static struct svc_pool_map svc_pool_map = {
.mode = SVC_POOL_DEFAULT
};
static struct svc_pool_map svc_pool_map;
static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */
static int
__param_set_pool_mode(const char *val, struct svc_pool_map *m)
{
int err, mode;
mutex_lock(&svc_pool_map_mutex);
err = 0;
if (!strncmp(val, "auto", 4))
mode = SVC_POOL_AUTO;
else if (!strncmp(val, "global", 6))
mode = SVC_POOL_GLOBAL;
else if (!strncmp(val, "percpu", 6))
mode = SVC_POOL_PERCPU;
else if (!strncmp(val, "pernode", 7))
mode = SVC_POOL_PERNODE;
else
err = -EINVAL;
if (err)
goto out;
if (m->count == 0)
m->mode = mode;
else if (mode != m->mode)
err = -EBUSY;
out:
mutex_unlock(&svc_pool_map_mutex);
return err;
}
static int
param_set_pool_mode(const char *val, const struct kernel_param *kp)
{
struct svc_pool_map *m = kp->arg;
return __param_set_pool_mode(val, m);
}
/*
* Pool modes that were historically accepted. They no longer select
* anything: the pool mode is always pernode. The names are retained
* only so that writing a previously-valid value still succeeds.
*/
static const char * const pool_mode_names[] = {
"auto", "global", "percpu", "pernode",
};
int sunrpc_set_pool_mode(const char *val)
{
return __param_set_pool_mode(val, &svc_pool_map);
int idx = sysfs_match_string(pool_mode_names, val);
return idx < 0 ? idx : 0;
}
EXPORT_SYMBOL(sunrpc_set_pool_mode);
@@ -122,83 +76,31 @@ EXPORT_SYMBOL(sunrpc_set_pool_mode);
* @buf: where to write the current pool_mode
* @size: size of @buf
*
* Grab the current pool_mode from the svc_pool_map and write
* the resulting string to @buf. Returns the number of characters
* Write the pool_mode string to @buf. Returns the number of characters
* written to @buf (a'la snprintf()).
*/
int
sunrpc_get_pool_mode(char *buf, size_t size)
{
struct svc_pool_map *m = &svc_pool_map;
switch (m->mode)
{
case SVC_POOL_AUTO:
return snprintf(buf, size, "auto");
case SVC_POOL_GLOBAL:
return snprintf(buf, size, "global");
case SVC_POOL_PERCPU:
return snprintf(buf, size, "percpu");
case SVC_POOL_PERNODE:
return snprintf(buf, size, "pernode");
default:
return snprintf(buf, size, "%d", m->mode);
}
return snprintf(buf, size, "pernode");
}
EXPORT_SYMBOL(sunrpc_get_pool_mode);
static int
param_set_pool_mode(const char *val, const struct kernel_param *kp)
{
pr_notice_once("sunrpc: the pool_mode module parameter is deprecated and no longer has any effect; the pool mode is always 'pernode'\n");
return sunrpc_set_pool_mode(val);
}
static int
param_get_pool_mode(char *buf, const struct kernel_param *kp)
{
char str[16];
int len;
len = sunrpc_get_pool_mode(str, ARRAY_SIZE(str));
/* Ensure we have room for newline and NUL */
len = min_t(int, len, ARRAY_SIZE(str) - 2);
/* tack on the newline */
str[len] = '\n';
str[len + 1] = '\0';
return sysfs_emit(buf, "%s", str);
return sysfs_emit(buf, "pernode\n");
}
module_param_call(pool_mode, param_set_pool_mode, param_get_pool_mode,
&svc_pool_map, 0644);
/*
* Detect best pool mapping mode heuristically,
* according to the machine's topology.
*/
static int
svc_pool_map_choose_mode(void)
{
unsigned int node;
if (nr_online_nodes > 1) {
/*
* Actually have multiple NUMA nodes,
* so split pools on NUMA node boundaries
*/
return SVC_POOL_PERNODE;
}
node = first_online_node;
if (nr_cpus_node(node) > 2) {
/*
* Non-trivial SMP, or CONFIG_NUMA on
* non-NUMA hardware, e.g. with a generic
* x86_64 kernel on Xeons. In this case we
* want to divide the pools on cpu boundaries.
*/
return SVC_POOL_PERCPU;
}
/* default: one global pool */
return SVC_POOL_GLOBAL;
}
NULL, 0644);
/*
* Allocate the to_pool[] and pool_to[] arrays.
@@ -224,35 +126,7 @@ svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools)
}
/*
* Initialise the pool map for SVC_POOL_PERCPU mode.
* Returns number of pools or <0 on error.
*/
static int
svc_pool_map_init_percpu(struct svc_pool_map *m)
{
unsigned int maxpools = nr_cpu_ids;
unsigned int pidx = 0;
unsigned int cpu;
int err;
err = svc_pool_map_alloc_arrays(m, maxpools);
if (err)
return err;
for_each_online_cpu(cpu) {
BUG_ON(pidx >= maxpools);
m->to_pool[cpu] = pidx;
m->pool_to[pidx] = cpu;
pidx++;
}
/* cpus brought online later all get mapped to pool0, sorry */
return pidx;
};
/*
* Initialise the pool map for SVC_POOL_PERNODE mode.
* Initialise the pool map for one pool per NUMA node.
* Returns number of pools or <0 on error.
*/
static int
@@ -281,17 +155,16 @@ svc_pool_map_init_pernode(struct svc_pool_map *m)
/*
* Add a reference to the global map of cpus to pools (and
* Add a reference to the global map of nodes to pools (and
* vice versa) if pools are in use.
* Initialise the map if we're the first user.
* Returns the number of pools. If this is '1', no reference
* was taken.
* Returns the number of pools, or 0 on failure.
*/
static unsigned int
svc_pool_map_get(void)
{
struct svc_pool_map *m = &svc_pool_map;
int npools = -1;
int npools;
mutex_lock(&svc_pool_map_mutex);
if (m->count++) {
@@ -299,22 +172,11 @@ svc_pool_map_get(void)
return m->npools;
}
if (m->mode == SVC_POOL_AUTO)
m->mode = svc_pool_map_choose_mode();
switch (m->mode) {
case SVC_POOL_PERCPU:
npools = svc_pool_map_init_percpu(m);
break;
case SVC_POOL_PERNODE:
npools = svc_pool_map_init_pernode(m);
break;
}
npools = svc_pool_map_init_pernode(m);
if (npools <= 0) {
/* default, or memory allocation failure */
npools = 1;
m->mode = SVC_POOL_GLOBAL;
m->count = 0;
mutex_unlock(&svc_pool_map_mutex);
return 0;
}
m->npools = npools;
mutex_unlock(&svc_pool_map_mutex);
@@ -322,7 +184,7 @@ svc_pool_map_get(void)
}
/*
* Drop a reference to the global map of cpus to pools.
* Drop a reference to the global map of nodes to pools.
* When the last reference is dropped, the map data is
* freed; this allows the sysadmin to change the pool.
*/
@@ -346,14 +208,9 @@ static int svc_pool_map_get_node(unsigned int pidx)
{
const struct svc_pool_map *m = &svc_pool_map;
if (m->count) {
if (m->mode == SVC_POOL_PERCPU)
return cpu_to_node(m->pool_to[pidx]);
if (m->mode == SVC_POOL_PERNODE)
return m->pool_to[pidx];
}
return numa_mem_id();
return m->pool_to[pidx];
}
/*
* Set the given thread's cpus_allowed mask so that it
* will only run on cpus in the given pool.
@@ -365,57 +222,80 @@ svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx)
unsigned int node = m->pool_to[pidx];
/*
* The caller checks for sv_nrpools > 1, which
* The caller checks for more than one pool, which
* implies that we've been initialized.
*/
WARN_ON_ONCE(m->count == 0);
if (m->count == 0)
return;
switch (m->mode) {
case SVC_POOL_PERCPU:
{
set_cpus_allowed_ptr(task, cpumask_of(node));
break;
}
case SVC_POOL_PERNODE:
{
set_cpus_allowed_ptr(task, cpumask_of_node(node));
break;
}
}
set_cpus_allowed_ptr(task, cpumask_of_node(node));
}
/**
* svc_serv_nrpools - number of thread pools backing a service
* @serv: An RPC service
*
* Pooled services all share the global svc_pool_map, so their pool count
* is svc_pool_map.npools. Unpooled services have a single pool. Reading
* npools without svc_pool_map_mutex is safe: a pooled service holds a map
* reference for its whole lifetime, so npools is stable once set.
*
* Return value:
* The number of pools in @serv
*/
unsigned int svc_serv_nrpools(const struct svc_serv *serv)
{
return serv->sv_is_pooled ? svc_pool_map.npools : 1;
}
EXPORT_SYMBOL_GPL(svc_serv_nrpools);
/**
* svc_pool_for_cpu - Select pool to run a thread on this cpu
* @serv: An RPC service
*
* Use the active CPU and the svc_pool_map's mode setting to
* select the svc thread pool to use. Once initialized, the
* svc_pool_map does not change.
* Use the active CPU and the svc_pool_map to select the svc thread
* pool to use. Once initialized, the svc_pool_map does not change.
*
* Return value:
* A pointer to an svc_pool
*/
struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv)
{
unsigned int nrpools = svc_serv_nrpools(serv);
struct svc_pool_map *m = &svc_pool_map;
int cpu = raw_smp_processor_id();
unsigned int pidx = 0;
unsigned int pidx, i;
if (serv->sv_nrpools <= 1)
if (nrpools <= 1)
return serv->sv_pools;
switch (m->mode) {
case SVC_POOL_PERCPU:
pidx = m->to_pool[cpu];
break;
case SVC_POOL_PERNODE:
pidx = m->to_pool[cpu_to_node(cpu)];
break;
/*
* It's possible to have a pool with no threads. Userland can just set
* things up this way directly. Also, when threads are autodistributed
* they are spread evenly across the pools, but when there are fewer
* threads than pools some pools can end up with none.
*
* A transport enqueued on a threadless pool would never be picked up,
* since each thread only services its own pool. Fall back to the next
* populated pool, trading NUMA locality for a guarantee that the
* transport is serviced.
*/
pidx = m->to_pool[cpu_to_node(raw_smp_processor_id())];
for (i = 0; i < nrpools; i++) {
struct svc_pool *pool = &serv->sv_pools[pidx];
/* This is set under the service mutex and rarely ever
* changes. A data race here is harmless.
*/
if (data_race(pool->sp_nrthreads))
return pool;
if (++pidx >= nrpools)
pidx = 0;
}
return &serv->sv_pools[pidx % serv->sv_nrpools];
/* No pool has any threads; nothing can service the transport. */
return &serv->sv_pools[pidx];
}
static int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
@@ -476,6 +356,35 @@ __svc_init_bc(struct svc_serv *serv)
}
#endif
static int svc_pool_init_counters(struct svc_pool *pool)
{
int err;
err = percpu_counter_init(&pool->sp_messages_arrived, 0, GFP_KERNEL);
if (err)
return err;
err = percpu_counter_init(&pool->sp_sockets_queued, 0, GFP_KERNEL);
if (err)
goto err_sockets;
err = percpu_counter_init(&pool->sp_threads_woken, 0, GFP_KERNEL);
if (err)
goto err_threads;
return 0;
err_threads:
percpu_counter_destroy(&pool->sp_sockets_queued);
err_sockets:
percpu_counter_destroy(&pool->sp_messages_arrived);
return err;
}
static void svc_pool_destroy_counters(struct svc_pool *pool)
{
percpu_counter_destroy(&pool->sp_messages_arrived);
percpu_counter_destroy(&pool->sp_sockets_queued);
percpu_counter_destroy(&pool->sp_threads_woken);
}
/*
* Create an RPC service
*/
@@ -521,15 +430,13 @@ __svc_create(struct svc_program *prog, int nprogs, struct svc_stat *stats,
__svc_init_bc(serv);
serv->sv_nrpools = npools;
serv->sv_pools =
kzalloc_objs(struct svc_pool, serv->sv_nrpools);
serv->sv_pools = kzalloc_objs(struct svc_pool, npools);
if (!serv->sv_pools) {
kfree(serv);
return NULL;
}
for (i = 0; i < serv->sv_nrpools; i++) {
for (i = 0; i < npools; i++) {
struct svc_pool *pool = &serv->sv_pools[i];
dprintk("svc: initialising pool %u for %s\n",
@@ -540,12 +447,18 @@ __svc_create(struct svc_program *prog, int nprogs, struct svc_stat *stats,
INIT_LIST_HEAD(&pool->sp_all_threads);
init_llist_head(&pool->sp_idle_threads);
percpu_counter_init(&pool->sp_messages_arrived, 0, GFP_KERNEL);
percpu_counter_init(&pool->sp_sockets_queued, 0, GFP_KERNEL);
percpu_counter_init(&pool->sp_threads_woken, 0, GFP_KERNEL);
if (svc_pool_init_counters(pool))
goto out_err;
}
return serv;
out_err:
while (i--)
svc_pool_destroy_counters(&serv->sv_pools[i]);
kfree(serv->sv_pools);
kfree(serv);
return NULL;
}
/**
@@ -582,6 +495,9 @@ struct svc_serv *svc_create_pooled(struct svc_program *prog,
struct svc_serv *serv;
unsigned int npools = svc_pool_map_get();
if (!npools)
return NULL;
serv = __svc_create(prog, nprogs, stats, bufsize, npools, threadfn);
if (!serv)
goto out_err;
@@ -618,16 +534,15 @@ svc_destroy(struct svc_serv **servp)
cache_clean_deferred(serv);
for (i = 0; i < svc_serv_nrpools(serv); i++) {
struct svc_pool *pool = &serv->sv_pools[i];
svc_pool_destroy_counters(pool);
}
if (serv->sv_is_pooled)
svc_pool_map_put();
for (i = 0; i < serv->sv_nrpools; i++) {
struct svc_pool *pool = &serv->sv_pools[i];
percpu_counter_destroy(&pool->sp_messages_arrived);
percpu_counter_destroy(&pool->sp_sockets_queued);
percpu_counter_destroy(&pool->sp_threads_woken);
}
kfree(serv->sv_pools);
kfree(serv);
}
@@ -683,6 +598,15 @@ svc_release_buffer(struct svc_rqst *rqstp)
}
}
static void svc_rqst_free_rcu(struct rcu_head *head)
{
struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head);
kfree(rqstp->rq_resp);
kfree(rqstp->rq_argp);
kfree(rqstp);
}
static void
svc_rqst_free(struct svc_rqst *rqstp)
{
@@ -691,10 +615,8 @@ svc_rqst_free(struct svc_rqst *rqstp)
svc_release_buffer(rqstp);
if (rqstp->rq_scratch_folio)
folio_put(rqstp->rq_scratch_folio);
kfree(rqstp->rq_resp);
kfree(rqstp->rq_argp);
kfree(rqstp->rq_auth_data);
kfree_rcu(rqstp, rq_rcu_head);
call_rcu(&rqstp->rq_rcu_head, svc_rqst_free_rcu);
}
static struct svc_rqst *
@@ -711,7 +633,9 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
rqstp->rq_server = serv;
rqstp->rq_pool = pool;
rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0, node);
rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0,
node == NUMA_NO_NODE ?
numa_mem_id() : node);
if (!rqstp->rq_scratch_folio)
goto out_enomem;
@@ -800,7 +724,15 @@ int svc_new_thread(struct svc_serv *serv, struct svc_pool *pool)
int node;
int err = 0;
node = svc_pool_map_get_node(pool->sp_id);
/*
* Only pooled services hold a reference to the pool map, so only they
* may consult it. Unpooled services (e.g. lockd, the NFS callback)
* leave placement to the allocator.
*/
if (serv->sv_is_pooled)
node = svc_pool_map_get_node(pool->sp_id);
else
node = NUMA_NO_NODE;
rqstp = svc_prepare_thread(serv, pool, node);
if (!rqstp)
@@ -813,7 +745,7 @@ int svc_new_thread(struct svc_serv *serv, struct svc_pool *pool)
}
rqstp->rq_task = task;
if (serv->sv_nrpools > 1)
if (svc_serv_nrpools(serv) > 1)
svc_pool_map_set_cpumask(task, pool->sp_id);
svc_sock_update_bufs(serv);
@@ -922,6 +854,12 @@ EXPORT_SYMBOL_GPL(svc_set_pool_threads);
* are multiple pools then the new threads or victims will be distributed
* evenly among them.
*
* When @nrservs is non-zero but smaller than the number of pools, even
* distribution would leave some pools empty. Since each pool maps to a
* NUMA node and only services transports steered to that node, every
* pool is instead guaranteed at least one thread. The resulting total
* may therefore exceed @nrservs.
*
* Caller must ensure mutual exclusion between this and server startup or
* shutdown.
*
@@ -933,11 +871,22 @@ int
svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
unsigned int nrservs)
{
unsigned int base = nrservs / serv->sv_nrpools;
unsigned int remain = nrservs % serv->sv_nrpools;
unsigned int nrpools = svc_serv_nrpools(serv);
unsigned int base = nrservs / nrpools;
unsigned int remain = nrservs % nrpools;
int i, err = 0;
for (i = 0; i < serv->sv_nrpools; ++i) {
/*
* Don't let a pool sit empty while threads are being
* auto-distributed: a transport steered to its node would have
* nothing to service it. Every pool maps to a CPU-bearing node,
* so hand each one a thread. This may push the total above
* @nrservs.
*/
if (base == 0 && nrservs != 0)
remain = nrpools;
for (i = 0; i < nrpools; ++i) {
struct svc_pool *pool = &serv->sv_pools[i];
int threads = base;
@@ -954,6 +903,29 @@ svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
}
EXPORT_SYMBOL_GPL(svc_set_num_threads);
/**
* svc_serv_maxthreads - report a service's configured thread ceiling
* @serv: RPC service to query
*
* A pooled service sizes its threads dynamically, so the number of
* threads running at any moment tracks recent load rather than the
* service's capacity. The per-pool maximum is the stable figure a
* consumer should size against.
*
* The caller must keep @serv valid for the duration of the call.
*
* Return: the sum of every pool's maximum thread count.
*/
unsigned int svc_serv_maxthreads(const struct svc_serv *serv)
{
unsigned int i, max = 0;
for (i = 0; i < svc_serv_nrpools(serv); i++)
max += data_race(serv->sv_pools[i].sp_nrthrmax);
return max;
}
EXPORT_SYMBOL_GPL(svc_serv_maxthreads);
/**
* svc_rqst_replace_page - Replace one page in rq_respages[]
* @rqstp: svc_rqst with pages to replace
@@ -1372,8 +1344,13 @@ svc_generic_init_request(struct svc_rqst *rqstp,
memset(rqstp->rq_argp, 0, procp->pc_argzero);
memset(rqstp->rq_resp, 0, procp->pc_ressize);
/* Bump per-procedure stats counter */
this_cpu_inc(versp->vs_count[rqstp->rq_proc]);
/* Bump per-net per-procedure stats counter */
if (rqstp->rq_server->sv_stats &&
rqstp->rq_server->sv_stats->program == progp &&
rqstp->rq_server->sv_stats->vs_count &&
rqstp->rq_server->sv_stats->vs_count[rqstp->rq_vers])
this_cpu_inc(rqstp->rq_server->sv_stats->vs_count
[rqstp->rq_vers][rqstp->rq_proc]);
ret->dispatch = versp->vs_dispatch;
return rpc_success;
@@ -1386,6 +1363,60 @@ svc_generic_init_request(struct svc_rqst *rqstp,
}
EXPORT_SYMBOL_GPL(svc_generic_init_request);
/**
* svc_stat_alloc_counts - allocate per-netns per-version call count arrays
* @statp: svc_stat whose vs_count arrays should be allocated
*
* statp->program must be set before calling this.
*
* Returns zero on success, or a negative errno otherwise.
*/
int svc_stat_alloc_counts(struct svc_stat *statp)
{
struct svc_program *prog = statp->program;
unsigned int i;
statp->vs_count = kcalloc(prog->pg_nvers,
sizeof(unsigned long __percpu *),
GFP_KERNEL);
if (!statp->vs_count)
return -ENOMEM;
for (i = 0; i < prog->pg_nvers; i++) {
if (!prog->pg_vers[i])
continue;
statp->vs_count[i] = __alloc_percpu(prog->pg_vers[i]->vs_nproc *
sizeof(unsigned long),
sizeof(unsigned long));
if (!statp->vs_count[i])
goto err;
}
return 0;
err:
svc_stat_free_counts(statp);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(svc_stat_alloc_counts);
/**
* svc_stat_free_counts - free per-netns per-version call count arrays
* @statp: svc_stat whose vs_count arrays should be freed
*/
void svc_stat_free_counts(struct svc_stat *statp)
{
struct svc_program *prog = statp->program;
unsigned int i;
if (!statp->vs_count)
return;
for (i = 0; i < prog->pg_nvers; i++)
free_percpu(statp->vs_count[i]);
kfree(statp->vs_count);
statp->vs_count = NULL;
}
EXPORT_SYMBOL_GPL(svc_stat_free_counts);
/*
* Common routine for processing the RPC request.
*/

View File

@@ -1188,7 +1188,7 @@ static void svc_clean_up_xprts(struct svc_serv *serv, struct net *net)
struct svc_xprt *xprt;
int i;
for (i = 0; i < serv->sv_nrpools; i++) {
for (i = 0; i < svc_serv_nrpools(serv); i++) {
struct svc_pool *pool = &serv->sv_pools[i];
struct llist_node *q, **t1, *t2;
@@ -1517,7 +1517,7 @@ static void *svc_pool_stats_start(struct seq_file *m, loff_t *pos)
return SEQ_START_TOKEN;
if (!si->serv)
return NULL;
return pidx > si->serv->sv_nrpools ? NULL
return pidx > svc_serv_nrpools(si->serv) ? NULL
: &si->serv->sv_pools[pidx - 1];
}
@@ -1535,7 +1535,7 @@ static void *svc_pool_stats_next(struct seq_file *m, void *p, loff_t *pos)
pool = &serv->sv_pools[0];
} else {
unsigned int pidx = (pool - &serv->sv_pools[0]);
if (pidx < serv->sv_nrpools-1)
if (pidx < svc_serv_nrpools(serv) - 1)
pool = &serv->sv_pools[pidx+1];
else
pool = NULL;

View File

@@ -133,7 +133,7 @@ static void ip_map_init(struct cache_head *cnew, struct cache_head *citem)
struct ip_map *new = container_of(cnew, struct ip_map, h);
struct ip_map *item = container_of(citem, struct ip_map, h);
strcpy(new->m_class, item->m_class);
strscpy(new->m_class, item->m_class);
new->m_addr = item->m_addr;
}
static void update(struct cache_head *cnew, struct cache_head *citem)
@@ -296,7 +296,7 @@ static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
struct ip_map ip;
struct cache_head *ch;
strcpy(ip.m_class, class);
strscpy(ip.m_class, class);
ip.m_addr = *addr;
ch = sunrpc_cache_lookup_rcu(cd, &ip.h,
hash_str(class, IP_HASHBITS) ^

View File

@@ -2049,7 +2049,7 @@ void xdr_buf_trim(struct xdr_buf *buf, unsigned int len)
trim -= cur;
}
fix_len:
buf->len -= (len - trim);
buf->len -= min_t(unsigned int, buf->len, len - trim);
}
EXPORT_SYMBOL_GPL(xdr_buf_trim);

View File

@@ -51,7 +51,11 @@ static struct rpcrdma_device *rpcrdma_get_client_data(struct ib_device *device)
* to be invoked when the device is removed, unless this notification
* is unregistered first.
*
* On failure, a negative errno is returned.
* On failure, a negative errno is returned. rn->rn_done is left
* NULL on every failure path (it is armed before xa_alloc but
* cleared again if xa_alloc fails), so the @rn may safely be
* passed to rpcrdma_rn_unregister() without a separate
* registered/unregistered flag in the caller.
*/
int rpcrdma_rn_register(struct ib_device *device,
struct rpcrdma_notification *rn,
@@ -62,10 +66,21 @@ int rpcrdma_rn_register(struct ib_device *device,
if (!rd || test_bit(RPCRDMA_RD_F_REMOVING, &rd->rd_flags))
return -ENETUNREACH;
if (xa_alloc(&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b, GFP_KERNEL) < 0)
return -ENOMEM;
kref_get(&rd->rd_kref);
/*
* Arm rn_done before xa_alloc() publishes @rn: once @rn is
* visible in rd_xa, a concurrent rpcrdma_remove_one() can
* call rn->rn_done(), so the pointer must already be set.
*
* Restore NULL if xa_alloc() fails. rn_done doubles as the
* registration sentinel for rpcrdma_rn_unregister(); a stale
* value would unregister an @rn that was never inserted.
*/
rn->rn_done = done;
if (xa_alloc(&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b, GFP_KERNEL) < 0) {
rn->rn_done = NULL;
return -ENOMEM;
}
kref_get(&rd->rd_kref);
trace_rpcrdma_client_register(device, rn);
return 0;
}
@@ -83,6 +98,10 @@ static void rpcrdma_rn_release(struct kref *kref)
* rpcrdma_rn_unregister - stop device removal notifications
* @device: monitored device
* @rn: notification object that no longer wishes to be notified
*
* It is safe to call this on an @rn whose registration never
* completed or failed; rn_done == NULL is treated as
* never-registered and the call is a no-op.
*/
void rpcrdma_rn_unregister(struct ib_device *device,
struct rpcrdma_notification *rn)
@@ -92,6 +111,21 @@ void rpcrdma_rn_unregister(struct ib_device *device,
if (!rd)
return;
/*
* rn_done is the registration sentinel: rpcrdma_rn_register
* leaves it NULL on every failure path, clearing it again if
* xa_alloc fails, so a non-NULL rn_done marks a completed
* registration. A NULL rn_done means this notification was
* never registered (or its registration failed) or has
* already been unregistered, and the call is a no-op.
* Without this guard, rn_index == 0 from a kzalloc'd
* parent would erase another caller's slot 0 and underflow
* rd_kref.
*/
if (!rn->rn_done)
return;
rn->rn_done = NULL;
trace_rpcrdma_client_unregister(device, rn);
xa_erase(&rd->rd_xa, rn->rn_index);
kref_put(&rd->rd_kref, rpcrdma_rn_release);

View File

@@ -149,9 +149,6 @@ bool pcl_alloc_call(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
* cl_count is updated to be the number of chunks (ie.
* unique position values) in the Read list.
* %false: Memory allocation failed.
*
* TODO:
* - Check for chunk range overlaps
*/
bool pcl_alloc_read(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
{
@@ -213,7 +210,6 @@ bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt,
chunk = pcl_alloc_chunk(segcount, 0);
if (!chunk)
return false;
list_add_tail(&chunk->ch_list, &pcl->cl_chunks);
for (j = 0; j < segcount; j++) {
segment = &chunk->ch_segments[j];
@@ -225,10 +221,69 @@ bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt,
chunk->ch_length += segment->rs_length;
chunk->ch_segcount++;
}
list_add_tail(&chunk->ch_list, &pcl->cl_chunks);
}
return true;
}
/**
* pcl_check_read_chunk_positions - Validate Read chunk positions
* @rctxt: Ingress receive context with populated chunk lists
* @inline_len: Length of the inline RPC body after the transport header
*
* Read chunk positions are offsets in the unreduced XDR stream
* (RFC 8166 Section 3.4.4), so each position includes the
* cumulative length of preceding Read chunks. This function
* subtracts those lengths to recover the inline-body offset
* before comparing against @inline_len or the Call chunk length.
*
* Rejects frames where a Read chunk's inline-body offset exceeds
* the bound, where adjacent Read chunks overlap, or where any
* single chunk length exceeds the page budget.
*
* Return values:
* %true: Read chunk positions and lengths are valid
* %false: Malformed chunk list detected
*/
bool pcl_check_read_chunk_positions(struct svc_rdma_recv_ctxt *rctxt,
unsigned int inline_len)
{
unsigned int max_len, bound, total_read;
struct svc_rdma_chunk *chunk, *next;
max_len = rctxt->rc_maxpages << PAGE_SHIFT;
if (!pcl_is_empty(&rctxt->rc_call_pcl)) {
chunk = pcl_first_chunk(&rctxt->rc_call_pcl);
if (chunk->ch_length > max_len)
return false;
bound = chunk->ch_length;
} else {
bound = inline_len;
}
if (pcl_is_empty(&rctxt->rc_read_pcl))
return true;
total_read = 0;
pcl_for_each_chunk(chunk, &rctxt->rc_read_pcl) {
if (chunk->ch_position - total_read > bound)
return false;
if (chunk->ch_length > max_len)
return false;
next = pcl_next_chunk(&rctxt->rc_read_pcl, chunk);
if (!next)
break;
if (chunk->ch_position + chunk->ch_length > next->ch_position)
return false;
total_read += chunk->ch_length;
}
return true;
}
static int pcl_process_region(const struct xdr_buf *xdr,
unsigned int offset, unsigned int length,
int (*actor)(const struct xdr_buf *, void *),

View File

@@ -440,11 +440,14 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp,
* to the first byte past the Read list. rc_read_pcl and
* rc_call_pcl cl_count fields are set to the number of
* Read segments in the list.
* %false: Read list is corrupt. @rctxt's xdr_stream is left in an
* unknown state.
* %false: Read list is corrupt or exceeds the page budget. @rctxt's
* xdr_stream is left in an unknown state.
*/
static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
{
unsigned int maxlen = rctxt->rc_maxpages << PAGE_SHIFT;
unsigned int total_len = 0;
rctxt->rc_call_pcl.cl_count = 0;
rctxt->rc_read_pcl.cl_count = 0;
while (xdr_item_is_present(p)) {
@@ -458,6 +461,11 @@ static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
xdr_decode_read_segment(p, &position, &handle,
&length, &offset);
if (length > maxlen)
return false;
total_len += length;
if (PAGE_ALIGN(total_len) > maxlen)
return false;
if (position) {
if (position & 3)
return false;
@@ -508,10 +516,13 @@ static bool xdr_check_write_chunk(struct svc_rdma_recv_ctxt *rctxt)
return false;
/* Before trusting the segcount value enough to use it in
* a computation, perform a simple range check. This is an
* arbitrary but sensible limit (ie, not architectural).
* a computation, perform a simple range check. A zero
* segcount describes no remote buffer and is rejected so
* downstream consumers never see a degenerate ch_segcount==0
* chunk. The upper bound is an arbitrary but sensible limit
* (ie, not architectural).
*/
if (unlikely(segcount > rctxt->rc_maxpages))
if (segcount == 0 || unlikely(segcount > rctxt->rc_maxpages))
return false;
p = xdr_inline_decode(&rctxt->rc_stream,
@@ -719,6 +730,9 @@ static int svc_rdma_xdr_decode_req(struct xdr_buf *rq_arg,
rq_arg->head[0].iov_base = rctxt->rc_stream.p;
hdr_len = xdr_stream_pos(&rctxt->rc_stream);
if (!pcl_check_read_chunk_positions(rctxt,
rq_arg->head[0].iov_len - hdr_len))
goto out_inval;
rq_arg->head[0].iov_len -= hdr_len;
rq_arg->len -= hdr_len;
trace_svcrdma_decode_rqst(rctxt, rdma_argp, hdr_len);

View File

@@ -795,7 +795,7 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
len -= seg_len;
if (len && ((head->rc_curpage + 1) > rqstp->rq_maxpages))
goto out_overrun;
goto out_put;
}
ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset,
@@ -809,7 +809,8 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
cc->cc_sqecount += ret;
return 0;
out_overrun:
out_put:
svc_rdma_put_rw_ctxt(rdma, ctxt);
trace_svcrdma_page_overrun_err(&cc->cc_cid, head->rc_curpage);
return -EINVAL;
}
@@ -847,7 +848,7 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp,
* svc_rdma_copy_inline_range - Copy part of the inline content into pages
* @rqstp: RPC transaction context
* @head: context for ongoing I/O
* @offset: offset into the Receive buffer of region to copy
* @offset: offset into the inline content of region to copy
* @remaining: length of region to copy
*
* Take a page at a time from rqstp->rq_pages and copy the inline
@@ -864,9 +865,13 @@ static int svc_rdma_copy_inline_range(struct svc_rqst *rqstp,
unsigned int offset,
unsigned int remaining)
{
unsigned char *dst, *src = head->rc_recv_buf;
unsigned char *dst, *src = head->rc_saved_arg.head[0].iov_base;
unsigned int inline_len = head->rc_saved_arg.head[0].iov_len;
unsigned int page_no, numpages;
if (offset > inline_len || remaining > inline_len - offset)
return -EINVAL;
numpages = PAGE_ALIGN(head->rc_pageoff + remaining) >> PAGE_SHIFT;
for (page_no = 0; page_no < numpages; page_no++) {
unsigned int page_len;
@@ -917,9 +922,10 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
{
const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;
struct svc_rdma_chunk *chunk, *next;
unsigned int start, length;
unsigned int inline_len, start, length;
int ret;
inline_len = head->rc_saved_arg.head[0].iov_len;
start = 0;
chunk = pcl_first_chunk(pcl);
length = chunk->ch_position;
@@ -937,6 +943,8 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
break;
start += length;
if (head->rc_readbytes > next->ch_position)
return -EINVAL;
length = next->ch_position - head->rc_readbytes;
ret = svc_rdma_copy_inline_range(rqstp, head, start, length);
if (ret < 0)
@@ -944,7 +952,9 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
}
start += length;
length = head->rc_byte_len - start;
if (start > inline_len)
return -EINVAL;
length = inline_len - start;
return svc_rdma_copy_inline_range(rqstp, head, start, length);
}
@@ -969,8 +979,12 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
static int svc_rdma_read_data_item(struct svc_rqst *rqstp,
struct svc_rdma_recv_ctxt *head)
{
return svc_rdma_build_read_chunk(rqstp, head,
pcl_first_chunk(&head->rc_read_pcl));
struct svc_rdma_chunk *chunk = pcl_first_chunk(&head->rc_read_pcl);
if (chunk->ch_position > head->rc_saved_arg.head[0].iov_len)
return -EINVAL;
return svc_rdma_build_read_chunk(rqstp, head, chunk);
}
/**
@@ -996,17 +1010,20 @@ static int svc_rdma_read_chunk_range(struct svc_rqst *rqstp,
const struct svc_rdma_segment *segment;
int ret;
if (!length)
return 0;
ret = -EINVAL;
pcl_for_each_segment(segment, chunk) {
struct svc_rdma_segment dummy;
if (offset > segment->rs_length) {
if (offset >= segment->rs_length) {
offset -= segment->rs_length;
continue;
}
dummy.rs_handle = segment->rs_handle;
dummy.rs_length = min_t(u32, length, segment->rs_length) - offset;
dummy.rs_length = min_t(u32, length, segment->rs_length - offset);
dummy.rs_offset = segment->rs_offset + offset;
ret = svc_rdma_build_read_segment(rqstp, head, &dummy);
@@ -1015,6 +1032,8 @@ static int svc_rdma_read_chunk_range(struct svc_rqst *rqstp,
head->rc_readbytes += dummy.rs_length;
length -= dummy.rs_length;
if (!length)
break;
offset = 0;
}
return ret;
@@ -1039,14 +1058,17 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,
pcl_first_chunk(&head->rc_call_pcl);
const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;
struct svc_rdma_chunk *chunk, *next;
unsigned int start, length;
unsigned int call_len, start, length;
int ret;
if (pcl_is_empty(pcl))
return svc_rdma_build_read_chunk(rqstp, head, call_chunk);
call_len = call_chunk->ch_length;
start = 0;
chunk = pcl_first_chunk(pcl);
if (chunk->ch_position > call_len)
return -EINVAL;
length = chunk->ch_position;
ret = svc_rdma_read_chunk_range(rqstp, head, call_chunk,
start, length);
@@ -1063,6 +1085,10 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,
break;
start += length;
if (next->ch_position > call_len)
return -EINVAL;
if (head->rc_readbytes > next->ch_position)
return -EINVAL;
length = next->ch_position - head->rc_readbytes;
ret = svc_rdma_read_chunk_range(rqstp, head, call_chunk,
start, length);
@@ -1071,7 +1097,9 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,
}
start += length;
length = call_chunk->ch_length - start;
if (start > call_len)
return -EINVAL;
length = call_len - start;
return svc_rdma_read_chunk_range(rqstp, head, call_chunk,
start, length);
}

View File

@@ -825,20 +825,21 @@ static int svc_rdma_xb_count_sges(const struct xdr_buf *xdr,
}
/**
* svc_rdma_pull_up_needed - Determine whether to use pull-up
* svc_rdma_check_pull_up - Determine whether to use pull-up
* @rdma: controlling transport
* @sctxt: send_ctxt for the Send WR
* @write_pcl: Write chunk list provided by client
* @xdr: xdr_buf containing RPC message to transmit
*
* Returns:
* %true if pull-up must be used
* %false otherwise
* %1 if pull-up must be used
* %0 if pull-up is not needed
* %-E2BIG if the reply is too large to be pulled up
*/
static bool svc_rdma_pull_up_needed(const struct svcxprt_rdma *rdma,
const struct svc_rdma_send_ctxt *sctxt,
const struct svc_rdma_pcl *write_pcl,
const struct xdr_buf *xdr)
static int svc_rdma_check_pull_up(const struct svcxprt_rdma *rdma,
const struct svc_rdma_send_ctxt *sctxt,
const struct svc_rdma_pcl *write_pcl,
const struct xdr_buf *xdr)
{
/* Resources needed for the transport header */
struct svc_rdma_pullup_data args = {
@@ -850,11 +851,22 @@ static bool svc_rdma_pull_up_needed(const struct svcxprt_rdma *rdma,
ret = pcl_process_nonpayloads(write_pcl, xdr,
svc_rdma_xb_count_sges, &args);
if (ret < 0)
return false;
return 0;
if (args.pd_length < RPCRDMA_PULLUP_THRESH)
return true;
return args.pd_num_sges >= rdma->sc_max_send_sges;
return 1;
if (args.pd_num_sges < rdma->sc_max_send_sges)
return 0;
/*
* The reply has too many SGEs to Send inline, so it has to be
* linearized into sc_xprt_buf. That buffer holds only
* sc_max_req_size bytes, so a larger reply cannot be pulled up.
* RFC 8166 Section 4.5.3 requires responding with ERR_CHUNK.
*/
if (args.pd_length > rdma->sc_max_req_size)
return -E2BIG;
return 1;
}
/**
@@ -910,7 +922,7 @@ static int svc_rdma_xb_linearize(const struct xdr_buf *xdr,
* Assemble the elements of @xdr into the transport header buffer.
*
* Assumptions:
* pull_up_needed has determined that @xdr will fit in the buffer.
* check_pull_up has determined that @xdr will fit in the buffer.
*
* Returns:
* %0 if pull-up was successful
@@ -945,6 +957,7 @@ static int svc_rdma_pull_up_reply_msg(const struct svcxprt_rdma *rdma,
*
* Returns:
* %0 if DMA mapping was successful.
* %-E2BIG if the reply is too large to be pulled up
* %-EMSGSIZE if a buffer manipulation problem occurred
* %-EIO if DMA mapping failed
*
@@ -960,6 +973,7 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
.md_rdma = rdma,
.md_ctxt = sctxt,
};
int ret;
/* Set up the (persistently-mapped) transport header SGE. */
sctxt->sc_send_wr.num_sge = 1;
@@ -974,7 +988,10 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
/* For pull-up, svc_rdma_send() will sync the transport header.
* No additional DMA mapping is necessary.
*/
if (svc_rdma_pull_up_needed(rdma, sctxt, write_pcl, xdr))
ret = svc_rdma_check_pull_up(rdma, sctxt, write_pcl, xdr);
if (ret < 0)
return ret;
if (ret)
return svc_rdma_pull_up_reply_msg(rdma, sctxt, write_pcl, xdr);
return pcl_process_nonpayloads(write_pcl, xdr,
@@ -1162,7 +1179,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
&rctxt->rc_reply_pcl, sctxt,
&rqstp->rq_res);
if (ret < 0)
goto reply_chunk;
goto send_err;
rc_size = ret;
}
@@ -1183,10 +1200,10 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
ret = svc_rdma_send_reply_msg(rdma, sctxt, rctxt, rqstp);
if (ret < 0)
goto put_ctxt;
goto send_err;
return 0;
reply_chunk:
send_err:
if (ret != -E2BIG && ret != -EINVAL)
goto put_ctxt;

View File

@@ -43,6 +43,7 @@
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -245,12 +246,16 @@ svc_rdma_parse_connect_private(struct svcxprt_rdma *newxprt,
* structure for the listening endpoint.
*
* This function creates a new xprt for the new connection and enqueues it on
* the accept queue for the listent xprt. When the listen thread is kicked, it
* the accept queue for the listen xprt. When the listen thread is kicked, it
* will call the recvfrom method on the listen xprt which will accept the new
* connection.
*
* Return values:
* %0: Do not destroy @new_cma_id
* %1: Destroy @new_cma_id (allocation failure)
*/
static void handle_connect_req(struct rdma_cm_id *new_cma_id,
struct rdma_conn_param *param)
static int handle_connect_req(struct rdma_cm_id *new_cma_id,
struct rdma_conn_param *param)
{
struct svcxprt_rdma *listen_xprt = new_cma_id->context;
struct svcxprt_rdma *newxprt;
@@ -260,7 +265,7 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id,
listen_xprt->sc_xprt.xpt_net,
ibdev_to_node(new_cma_id->device));
if (!newxprt)
return;
return 1;
newxprt->sc_cm_id = new_cma_id;
new_cma_id->context = newxprt;
svc_rdma_parse_connect_private(newxprt, param);
@@ -294,6 +299,7 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id,
set_bit(XPT_CONN, &listen_xprt->sc_xprt.xpt_flags);
svc_xprt_enqueue(&listen_xprt->sc_xprt);
return 0;
}
/**
@@ -317,14 +323,14 @@ static int svc_rdma_listen_handler(struct rdma_cm_id *cma_id,
switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST:
handle_connect_req(cma_id, &event->param.conn);
break;
return handle_connect_req(cma_id, &event->param.conn);
case RDMA_CM_EVENT_ADDR_CHANGE:
listen_id = svc_rdma_create_listen_id(cma_rdma->xpt_net,
sap, cma_xprt);
if (IS_ERR(listen_id)) {
pr_err("Listener dead, address change failed for device %s\n",
cma_id->device->name);
cma_xprt->sc_cm_id = NULL;
} else
cma_xprt->sc_cm_id = listen_id;
return 1;
@@ -387,7 +393,13 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
listen_id = svc_rdma_create_listen_id(net, sa, cma_xprt);
if (IS_ERR(listen_id)) {
kfree(cma_xprt);
/* _svc_xprt_create() acquired one module reference and
* puts it on xpo_create failure. svc_xprt_free() puts
* a second one when the kref drops to zero. Take a
* compensating reference so both puts are balanced.
*/
__module_get(cma_xprt->sc_xprt.xpt_class->xcl_owner);
svc_xprt_put(&cma_xprt->sc_xprt);
return ERR_CAST(listen_id);
}
cma_xprt->sc_cm_id = listen_id;
@@ -598,13 +610,26 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
return &newxprt->sc_xprt;
errout:
/* Take a reference in case the DTO handler runs */
svc_xprt_get(&newxprt->sc_xprt);
if (newxprt->sc_qp && !IS_ERR(newxprt->sc_qp))
ib_destroy_qp(newxprt->sc_qp);
rdma_destroy_id(newxprt->sc_cm_id);
rpcrdma_rn_unregister(dev, &newxprt->sc_rn);
/* This call to put will destroy the transport */
/*
* Drop the kref_init birth reference. svc_xprt_free will
* dispatch xpo_free = svc_rdma_free, which tears down sc_qp,
* sc_sq_cq, sc_rq_cq, and sc_pd under existing IS_ERR/NULL
* guards, and sc_rn under the rn_done sentinel guard inside
* rpcrdma_rn_unregister.
*
* sc_cm_id is destroyed unconditionally by svc_rdma_free; that
* is safe here because sc_cm_id is non-NULL by caller invariant
* on every path that reaches this errout: handle_connect_req
* installs newxprt->sc_cm_id before queueing the new xprt for
* accept, and svc_rdma_accept has already dereferenced it above
* the first goto errout.
*
* svc_handle_xprt() drops its pre-acquired module reference when
* ->xpo_accept() returns NULL. Take a replacement reference before
* freeing @newxprt, because svc_xprt_free() drops the module
* reference associated with @newxprt.
*/
__module_get(newxprt->sc_xprt.xpt_class->xcl_owner);
svc_xprt_put(&newxprt->sc_xprt);
return NULL;
}
@@ -614,7 +639,8 @@ static void svc_rdma_detach(struct svc_xprt *xprt)
struct svcxprt_rdma *rdma =
container_of(xprt, struct svcxprt_rdma, sc_xprt);
rdma_disconnect(rdma->sc_cm_id);
if (rdma->sc_cm_id)
rdma_disconnect(rdma->sc_cm_id);
/*
* Most close paths go through svc_rdma_xprt_deferred_close(),
@@ -634,10 +660,15 @@ static void svc_rdma_free(struct svc_xprt *xprt)
{
struct svcxprt_rdma *rdma =
container_of(xprt, struct svcxprt_rdma, sc_xprt);
struct ib_device *device = rdma->sc_cm_id->device;
struct ib_device *device;
might_sleep();
if (!rdma->sc_cm_id)
goto out_free;
device = rdma->sc_cm_id->device;
/* This blocks until the Completion Queues are empty */
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
ib_drain_qp(rdma->sc_qp);
@@ -662,11 +693,13 @@ static void svc_rdma_free(struct svc_xprt *xprt)
if (rdma->sc_pd && !IS_ERR(rdma->sc_pd))
ib_dealloc_pd(rdma->sc_pd);
if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags))
rpcrdma_rn_unregister(device, &rdma->sc_rn);
/* Destroy the CM ID */
rdma_destroy_id(rdma->sc_cm_id);
if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags))
rpcrdma_rn_unregister(device, &rdma->sc_rn);
out_free:
kfree(rdma);
}

View File

@@ -38,6 +38,8 @@ def emit_version_declarations(
arguments = dict.fromkeys([])
for procedure in version.procedures:
if procedure.name not in excluded_apis:
if procedure.argument.type_name == "void":
continue
arguments[procedure.argument.type_name] = None
if len(arguments) > 0:
print("")
@@ -48,6 +50,8 @@ def emit_version_declarations(
results = dict.fromkeys([])
for procedure in version.procedures:
if procedure.name not in excluded_apis:
if procedure.result.type_name == "void":
continue
results[procedure.result.type_name] = None
if len(results) > 0:
print("")
@@ -63,6 +67,8 @@ def emit_version_argument_decoders(
arguments = dict.fromkeys([])
for procedure in version.procedures:
if procedure.name not in excluded_apis:
if procedure.argument.type_name == "void":
continue
arguments[procedure.argument.type_name] = None
template = environment.get_template("decoder/argument.j2")
@@ -105,6 +111,8 @@ def emit_version_result_encoders(
results = dict.fromkeys([])
for procedure in version.procedures:
if procedure.name not in excluded_apis:
if procedure.result.type_name == "void":
continue
results[procedure.result.type_name] = None
template = environment.get_template("encoder/result.j2")

View File

@@ -21,16 +21,15 @@ from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, _RpcProgram, Specification
from xdr_ast import _XdrEnum, _XdrPointer, _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_ast import XdrSemanticError
from xdr_parse import xdr_parser, set_xdr_annotate
from xdr_parse import make_error_handler, XdrParseError
from xdr_parse import handle_transform_error
from xdr_parse import handle_transform_error, handle_semantic_error
logger.setLevel(logging.INFO)
def emit_header_declarations(
root: Specification, language: str, peer: str
) -> None:
def emit_header_declarations(root: Specification, language: str, peer: str) -> None:
"""Emit header declarations"""
for definition in root.definitions:
if isinstance(definition.value, _XdrEnum):
@@ -68,6 +67,9 @@ def subcmd(args: Namespace) -> int:
except VisitError as e:
handle_transform_error(e, source, args.filename)
return 1
except XdrSemanticError as e:
handle_semantic_error(e, source, args.filename)
return 1
gen = XdrHeaderTopGenerator(args.language, args.peer)
gen.emit_declaration(args.filename, ast)

View File

@@ -21,12 +21,12 @@ from generators.typedef import XdrTypedefGenerator
from generators.struct import XdrStructGenerator
from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, Specification
from xdr_ast import transform_parse_tree, Specification, XdrSemanticError
from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPassthru, _XdrPointer
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
from xdr_parse import make_error_handler, XdrParseError
from xdr_parse import handle_transform_error
from xdr_parse import handle_transform_error, handle_semantic_error
logger.setLevel(logging.INFO)
@@ -94,6 +94,9 @@ def subcmd(args: Namespace) -> int:
except VisitError as e:
handle_transform_error(e, source, args.filename)
return 1
except XdrSemanticError as e:
handle_semantic_error(e, source, args.filename)
return 1
gen = XdrHeaderTopGenerator(args.language, args.peer)
gen.emit_definition(args.filename, ast)

View File

@@ -11,8 +11,8 @@ from lark import logger
from lark.exceptions import VisitError
from xdr_parse import xdr_parser, make_error_handler, XdrParseError
from xdr_parse import handle_transform_error
from xdr_ast import transform_parse_tree
from xdr_parse import handle_transform_error, handle_semantic_error
from xdr_ast import transform_parse_tree, XdrSemanticError
logger.setLevel(logging.DEBUG)
@@ -34,5 +34,8 @@ def subcmd(args: Namespace) -> int:
except VisitError as e:
handle_transform_error(e, source, args.filename)
return 1
except XdrSemanticError as e:
handle_semantic_error(e, source, args.filename)
return 1
return 0

Some files were not shown because too many files have changed in this diff Show More