summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap6
-rw-r--r--Documentation/netlink/specs/nfsd.yaml290
-rw-r--r--Documentation/netlink/specs/sunrpc_cache.yaml149
-rw-r--r--Documentation/sunrpc/xdr/nlm3.x168
-rw-r--r--MAINTAINERS8
-rw-r--r--fs/lockd/Makefile19
-rw-r--r--fs/lockd/clnt4xdr.c42
-rw-r--r--fs/lockd/clntlock.c2
-rw-r--r--fs/lockd/clntproc.c14
-rw-r--r--fs/lockd/clntxdr.c44
-rw-r--r--fs/lockd/host.c4
-rw-r--r--fs/lockd/lockd.h65
-rw-r--r--fs/lockd/mon.c2
-rw-r--r--fs/lockd/nlm3xdr_gen.c714
-rw-r--r--fs/lockd/nlm3xdr_gen.h32
-rw-r--r--fs/lockd/share.h4
-rw-r--r--fs/lockd/svc4proc.c72
-rw-r--r--fs/lockd/svclock.c38
-rw-r--r--fs/lockd/svcproc.c1712
-rw-r--r--fs/lockd/svcshare.c8
-rw-r--r--fs/lockd/svcsubs.c9
-rw-r--r--fs/lockd/svcxdr.h142
-rw-r--r--fs/lockd/trace.h16
-rw-r--r--fs/lockd/xdr.c354
-rw-r--r--fs/lockd/xdr.h39
-rw-r--r--fs/nfs/dns_resolve.c2
-rw-r--r--fs/nfsd/export.c777
-rw-r--r--fs/nfsd/export.h7
-rw-r--r--fs/nfsd/filecache.c46
-rw-r--r--fs/nfsd/filecache.h1
-rw-r--r--fs/nfsd/netlink.c129
-rw-r--r--fs/nfsd/netlink.h21
-rw-r--r--fs/nfsd/nfs2acl.c17
-rw-r--r--fs/nfsd/nfs3acl.c17
-rw-r--r--fs/nfsd/nfs4idmap.c4
-rw-r--r--fs/nfsd/nfs4layouts.c14
-rw-r--r--fs/nfsd/nfs4proc.c19
-rw-r--r--fs/nfsd/nfs4recover.c3
-rw-r--r--fs/nfsd/nfs4state.c267
-rw-r--r--fs/nfsd/nfs4xdr.c3
-rw-r--r--fs/nfsd/nfsctl.c213
-rw-r--r--fs/nfsd/nfsd.h22
-rw-r--r--fs/nfsd/state.h6
-rw-r--r--fs/nfsd/trace.h32
-rw-r--r--fs/nfsd/vfs.c12
-rw-r--r--include/linux/sunrpc/cache.h15
-rw-r--r--include/linux/sunrpc/gss_krb5.h105
-rw-r--r--include/linux/sunrpc/svc_rdma.h7
-rw-r--r--include/linux/sunrpc/xdr.h20
-rw-r--r--include/linux/sunrpc/xdrgen/nlm3.h210
-rw-r--r--include/uapi/linux/nfsd_netlink.h156
-rw-r--r--include/uapi/linux/sunrpc_netlink.h84
-rw-r--r--net/sunrpc/.kunitconfig29
-rw-r--r--net/sunrpc/Kconfig56
-rw-r--r--net/sunrpc/Makefile2
-rw-r--r--net/sunrpc/auth_gss/Makefile4
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c978
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_internal.h155
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_keys.c546
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_mech.c433
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seal.c47
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_test.c1868
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c36
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_wrap.c13
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c2
-rw-r--r--net/sunrpc/cache.c127
-rw-r--r--net/sunrpc/netlink.c97
-rw-r--r--net/sunrpc/netlink.h35
-rw-r--r--net/sunrpc/sunrpc_syms.c10
-rw-r--r--net/sunrpc/svc.c10
-rw-r--r--net/sunrpc/svc_xprt.c46
-rw-r--r--net/sunrpc/svcauth_unix.c516
-rw-r--r--net/sunrpc/svcsock.c24
-rw-r--r--net/sunrpc/xdr.c289
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma.c18
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c13
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_rw.c240
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c97
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c33
79 files changed, 6128 insertions, 5758 deletions
diff --git a/.mailmap b/.mailmap
index 34b9b51e9e8c..33b03396b18f 100644
--- a/.mailmap
+++ b/.mailmap
@@ -202,9 +202,9 @@ Christophe Ricard <christophe.ricard@gmail.com>
Christopher Obbard <christopher.obbard@linaro.org> <chris.obbard@collabora.com>
Christoph Hellwig <hch@lst.de>
Christoph Manszewski <c.manszewski@gmail.com> <christoph.manszewski@intel.com>
-Chuck Lever <chuck.lever@oracle.com> <cel@kernel.org>
-Chuck Lever <chuck.lever@oracle.com> <cel@netapp.com>
-Chuck Lever <chuck.lever@oracle.com> <cel@citi.umich.edu>
+Chuck Lever <cel@kernel.org> <chuck.lever@oracle.com>
+Chuck Lever <cel@kernel.org> <cel@netapp.com>
+Chuck Lever <cel@kernel.org> <cel@citi.umich.edu>
Claudiu Beznea <claudiu.beznea@tuxon.dev> <claudiu.beznea@microchip.com>
Colin Ian King <colin.i.king@gmail.com> <colin.king@canonical.com>
Corey Minyard <minyard@acm.org>
diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
index 8ab43c8253b2..8f36fadd68f7 100644
--- a/Documentation/netlink/specs/nfsd.yaml
+++ b/Documentation/netlink/specs/nfsd.yaml
@@ -6,8 +6,52 @@ uapi-header: linux/nfsd_netlink.h
doc: NFSD configuration over generic netlink.
+definitions:
+ -
+ type: flags
+ name: cache-type
+ entries: [svc_export, expkey]
+ -
+ type: flags
+ name: export-flags
+ doc: These flags are ordered to match the NFSEXP_* flags in include/linux/nfsd/export.h
+ entries:
+ - readonly
+ - insecure-port
+ - rootsquash
+ - allsquash
+ - async
+ - gathered-writes
+ - noreaddirplus
+ - security-label
+ - sign-fh
+ - nohide
+ - nosubtreecheck
+ - noauthnlm
+ - msnfs
+ - fsid
+ - crossmount
+ - noacl
+ - v4root
+ - pnfs
+ -
+ type: flags
+ name: xprtsec-mode
+ doc: These flags are ordered to match the NFSEXP_XPRTSEC_* flags in include/linux/nfsd/export.h
+ entries:
+ - none
+ - tls
+ - mtls
+
attribute-sets:
-
+ name: cache-notify
+ attributes:
+ -
+ name: cache-type
+ type: u32
+ enum: cache-type
+ -
name: rpc-status
attributes:
-
@@ -132,6 +176,160 @@ attribute-sets:
-
name: npools
type: u32
+ -
+ name: fslocation
+ attributes:
+ -
+ name: host
+ type: string
+ -
+ name: path
+ type: string
+ -
+ name: fslocations
+ attributes:
+ -
+ name: location
+ type: nest
+ nested-attributes: fslocation
+ multi-attr: true
+ -
+ name: auth-flavor
+ attributes:
+ -
+ name: pseudoflavor
+ type: u32
+ -
+ name: flags
+ type: u32
+ enum: export-flags
+ enum-as-flags: true
+ -
+ name: svc-export
+ attributes:
+ -
+ name: seqno
+ type: u64
+ -
+ name: client
+ type: string
+ -
+ name: path
+ type: string
+ -
+ name: negative
+ type: flag
+ -
+ name: expiry
+ type: u64
+ -
+ name: anon-uid
+ type: u32
+ -
+ name: anon-gid
+ type: u32
+ -
+ name: fslocations
+ type: nest
+ nested-attributes: fslocations
+ -
+ name: uuid
+ type: binary
+ -
+ name: secinfo
+ type: nest
+ nested-attributes: auth-flavor
+ multi-attr: true
+ -
+ name: xprtsec
+ type: u32
+ enum: xprtsec-mode
+ multi-attr: true
+ -
+ name: flags
+ type: u32
+ enum: export-flags
+ enum-as-flags: true
+ -
+ name: fsid
+ type: s32
+ -
+ name: svc-export-reqs
+ attributes:
+ -
+ name: requests
+ type: nest
+ nested-attributes: svc-export
+ multi-attr: true
+ -
+ name: expkey
+ attributes:
+ -
+ name: seqno
+ type: u64
+ -
+ name: client
+ type: string
+ -
+ name: fsidtype
+ type: u8
+ -
+ name: fsid
+ type: binary
+ -
+ name: negative
+ type: flag
+ -
+ name: expiry
+ type: u64
+ -
+ name: path
+ type: string
+ -
+ name: expkey-reqs
+ attributes:
+ -
+ name: requests
+ type: nest
+ nested-attributes: expkey
+ multi-attr: true
+ -
+ name: cache-flush
+ attributes:
+ -
+ name: mask
+ type: u32
+ enum: cache-type
+ enum-as-flags: true
+ -
+ name: unlock-ip
+ attributes:
+ -
+ name: address
+ type: binary
+ doc: struct sockaddr_in or struct sockaddr_in6.
+ checks:
+ min-len: 16
+ -
+ name: unlock-filesystem
+ attributes:
+ -
+ name: path
+ type: string
+ doc: Filesystem path whose state should be released.
+ -
+ name: unlock-export
+ attributes:
+ -
+ name: path
+ type: string
+ doc: >-
+ Export path whose NFSv4 state should be revoked.
+ All state (opens, locks, delegations, layouts) acquired
+ through any export of this path is revoked, regardless
+ 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.
operations:
list:
@@ -233,3 +431,95 @@ operations:
attributes:
- mode
- npools
+ -
+ name: cache-notify
+ doc: Notification that there are cache requests that need servicing
+ attribute-set: cache-notify
+ mcgrp: exportd
+ event:
+ attributes:
+ - cache-type
+ -
+ name: svc-export-get-reqs
+ doc: Dump all pending svc_export requests
+ attribute-set: svc-export-reqs
+ flags: [admin-perm]
+ dump:
+ reply:
+ attributes:
+ - requests
+ -
+ name: svc-export-set-reqs
+ doc: Respond to one or more svc_export requests
+ attribute-set: svc-export-reqs
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - requests
+ -
+ name: expkey-get-reqs
+ doc: Dump all pending expkey requests
+ attribute-set: expkey-reqs
+ flags: [admin-perm]
+ dump:
+ reply:
+ attributes:
+ - requests
+ -
+ name: expkey-set-reqs
+ doc: Respond to one or more expkey requests
+ attribute-set: expkey-reqs
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - requests
+ -
+ name: cache-flush
+ doc: Flush nfsd caches (svc_export and/or expkey)
+ attribute-set: cache-flush
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - mask
+ -
+ name: unlock-ip
+ doc: release NLM locks held by an IP address
+ attribute-set: unlock-ip
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - address
+ -
+ name: unlock-filesystem
+ doc: revoke NFS state under a filesystem path
+ attribute-set: unlock-filesystem
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - path
+ -
+ name: unlock-export
+ doc: >-
+ Revoke NFSv4 state acquired through exports of a given path.
+ Unlike unlock-filesystem, which operates at superblock granularity,
+ this command targets only state associated with a specific export
+ path. Userspace (exportfs -u) sends this after removing the last
+ client for a path so the underlying filesystem can be unmounted.
+ attribute-set: unlock-export
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - path
+
+mcast-groups:
+ list:
+ -
+ name: none
+ -
+ name: exportd
diff --git a/Documentation/netlink/specs/sunrpc_cache.yaml b/Documentation/netlink/specs/sunrpc_cache.yaml
new file mode 100644
index 000000000000..f22ff22b9418
--- /dev/null
+++ b/Documentation/netlink/specs/sunrpc_cache.yaml
@@ -0,0 +1,149 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+---
+name: sunrpc
+protocol: genetlink
+uapi-header: linux/sunrpc_netlink.h
+
+doc: SUNRPC cache upcall support over generic netlink.
+
+definitions:
+ -
+ type: flags
+ name: cache-type
+ entries: [ip_map, unix_gid]
+
+attribute-sets:
+ -
+ name: cache-notify
+ attributes:
+ -
+ name: cache-type
+ type: u32
+ enum: cache-type
+ -
+ name: ip-map
+ attributes:
+ -
+ name: seqno
+ type: u64
+ -
+ name: class
+ type: string
+ -
+ name: addr
+ type: string
+ -
+ name: domain
+ type: string
+ -
+ name: negative
+ type: flag
+ -
+ name: expiry
+ type: u64
+ -
+ name: ip-map-reqs
+ attributes:
+ -
+ name: requests
+ type: nest
+ nested-attributes: ip-map
+ multi-attr: true
+ -
+ name: unix-gid
+ attributes:
+ -
+ name: seqno
+ type: u64
+ -
+ name: uid
+ type: u32
+ -
+ name: gids
+ type: u32
+ multi-attr: true
+ -
+ name: negative
+ type: flag
+ -
+ name: expiry
+ type: u64
+ -
+ name: unix-gid-reqs
+ attributes:
+ -
+ name: requests
+ type: nest
+ nested-attributes: unix-gid
+ multi-attr: true
+ -
+ name: cache-flush
+ attributes:
+ -
+ name: mask
+ type: u32
+ enum: cache-type
+ enum-as-flags: true
+
+operations:
+ list:
+ -
+ name: cache-notify
+ doc: Notification that there are cache requests that need servicing
+ attribute-set: cache-notify
+ mcgrp: exportd
+ event:
+ attributes:
+ - cache-type
+ -
+ name: ip-map-get-reqs
+ doc: Dump all pending ip_map requests
+ attribute-set: ip-map-reqs
+ flags: [admin-perm]
+ dump:
+ reply:
+ attributes:
+ - requests
+ -
+ name: ip-map-set-reqs
+ doc: Respond to one or more ip_map requests
+ attribute-set: ip-map-reqs
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - requests
+ -
+ name: unix-gid-get-reqs
+ doc: Dump all pending unix_gid requests
+ attribute-set: unix-gid-reqs
+ flags: [admin-perm]
+ dump:
+ reply:
+ attributes:
+ - requests
+ -
+ name: unix-gid-set-reqs
+ doc: Respond to one or more unix_gid requests
+ attribute-set: unix-gid-reqs
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - requests
+ -
+ name: cache-flush
+ doc: Flush sunrpc caches (ip_map and/or unix_gid)
+ attribute-set: cache-flush
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - mask
+
+mcast-groups:
+ list:
+ -
+ name: none
+ -
+ name: exportd
diff --git a/Documentation/sunrpc/xdr/nlm3.x b/Documentation/sunrpc/xdr/nlm3.x
new file mode 100644
index 000000000000..b2e704f7b864
--- /dev/null
+++ b/Documentation/sunrpc/xdr/nlm3.x
@@ -0,0 +1,168 @@
+/*
+ * This file was extracted by hand from
+ * https://pubs.opengroup.org/onlinepubs/9629799/chap10.htm#tagcjh_11_03
+ */
+
+/*
+ * The NLMv3 protocol
+ */
+
+pragma header nlm3;
+
+const LM_MAXSTRLEN = 1024;
+
+const LM_MAXNAMELEN = 1025;
+
+const MAXNETOBJ_SZ = 1024;
+
+typedef opaque netobj<MAXNETOBJ_SZ>;
+
+enum nlm_stats {
+ LCK_GRANTED = 0,
+ LCK_DENIED = 1,
+ LCK_DENIED_NOLOCKS = 2,
+ LCK_BLOCKED = 3,
+ LCK_DENIED_GRACE_PERIOD = 4
+};
+
+pragma big_endian nlm_stats;
+
+struct nlm_stat {
+ nlm_stats stat;
+};
+
+struct nlm_res {
+ netobj cookie;
+ nlm_stat stat;
+};
+
+struct nlm_holder {
+ bool exclusive;
+ int uppid;
+ netobj oh;
+ unsigned int l_offset;
+ unsigned int l_len;
+};
+
+union nlm_testrply switch (nlm_stats stat) {
+ case LCK_DENIED:
+ nlm_holder holder;
+ default:
+ void;
+};
+
+struct nlm_testres {
+ netobj cookie;
+ nlm_testrply test_stat;
+};
+
+struct nlm_lock {
+ string caller_name<LM_MAXSTRLEN>;
+ netobj fh;
+ netobj oh;
+ int uppid;
+ unsigned int l_offset;
+ unsigned int l_len;
+};
+
+struct nlm_lockargs {
+ netobj cookie;
+ bool block;
+ bool exclusive;
+ nlm_lock alock;
+ bool reclaim;
+ int state;
+};
+
+struct nlm_cancargs {
+ netobj cookie;
+ bool block;
+ bool exclusive;
+ nlm_lock alock;
+};
+
+struct nlm_testargs {
+ netobj cookie;
+ bool exclusive;
+ nlm_lock alock;
+};
+
+struct nlm_unlockargs {
+ netobj cookie;
+ nlm_lock alock;
+};
+
+enum fsh_mode {
+ fsm_DN = 0,
+ fsm_DR = 1,
+ fsm_DW = 2,
+ fsm_DRW = 3
+};
+
+enum fsh_access {
+ fsa_NONE = 0,
+ fsa_R = 1,
+ fsa_W = 2,
+ fsa_RW = 3
+};
+
+struct nlm_share {
+ string caller_name<LM_MAXSTRLEN>;
+ netobj fh;
+ netobj oh;
+ fsh_mode mode;
+ fsh_access access;
+};
+
+struct nlm_shareargs {
+ netobj cookie;
+ nlm_share share;
+ bool reclaim;
+};
+
+struct nlm_shareres {
+ netobj cookie;
+ nlm_stats stat;
+ int sequence;
+};
+
+struct nlm_notify {
+ string name<LM_MAXNAMELEN>;
+ long state;
+};
+
+/*
+ * Argument for the Linux-private SM_NOTIFY procedure
+ */
+const SM_PRIV_SIZE = 16;
+
+struct nlm_notifyargs {
+ nlm_notify notify;
+ opaque private[SM_PRIV_SIZE];
+};
+
+program NLM_PROG {
+ version NLM_VERS {
+ void NLM_NULL(void) = 0;
+ nlm_testres NLM_TEST(nlm_testargs) = 1;
+ nlm_res NLM_LOCK(nlm_lockargs) = 2;
+ nlm_res NLM_CANCEL(nlm_cancargs) = 3;
+ nlm_res NLM_UNLOCK(nlm_unlockargs) = 4;
+ nlm_res NLM_GRANTED(nlm_testargs) = 5;
+ void NLM_TEST_MSG(nlm_testargs) = 6;
+ void NLM_LOCK_MSG(nlm_lockargs) = 7;
+ void NLM_CANCEL_MSG(nlm_cancargs) = 8;
+ void NLM_UNLOCK_MSG(nlm_unlockargs) = 9;
+ void NLM_GRANTED_MSG(nlm_testargs) = 10;
+ void NLM_TEST_RES(nlm_testres) = 11;
+ void NLM_LOCK_RES(nlm_res) = 12;
+ void NLM_CANCEL_RES(nlm_res) = 13;
+ void NLM_UNLOCK_RES(nlm_res) = 14;
+ void NLM_GRANTED_RES(nlm_res) = 15;
+ void NLM_SM_NOTIFY(nlm_notifyargs) = 16;
+ nlm_shareres NLM_SHARE(nlm_shareargs) = 20;
+ nlm_shareres NLM_UNSHARE(nlm_shareargs) = 21;
+ nlm_res NLM_NM_LOCK(nlm_lockargs) = 22;
+ void NLM_FREE_ALL(nlm_notify) = 23;
+ } = 3;
+} = 100021;
diff --git a/MAINTAINERS b/MAINTAINERS
index bbf2c7715e29..635713a76fd4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9902,7 +9902,7 @@ F: include/uapi/scsi/fc/
FILE LOCKING (flock() and fcntl()/lockf())
M: Jeff Layton <jlayton@kernel.org>
-M: Chuck Lever <chuck.lever@oracle.com>
+M: Chuck Lever <cel@kernel.org>
R: Alexander Aring <alex.aring@gmail.com>
L: linux-fsdevel@vger.kernel.org
S: Maintained
@@ -9943,7 +9943,7 @@ F: init/do_mounts*
F: init/*initramfs*
FILESYSTEMS [EXPORTFS]
-M: Chuck Lever <chuck.lever@oracle.com>
+M: Chuck Lever <cel@kernel.org>
M: Jeff Layton <jlayton@kernel.org>
R: Amir Goldstein <amir73il@gmail.com>
L: linux-fsdevel@vger.kernel.org
@@ -11306,7 +11306,7 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
F: drivers/media/usb/hackrf/
HANDSHAKE UPCALL FOR TRANSPORT LAYER SECURITY
-M: Chuck Lever <chuck.lever@oracle.com>
+M: Chuck Lever <cel@kernel.org>
L: kernel-tls-handshake@lists.linux.dev
L: netdev@vger.kernel.org
S: Maintained
@@ -14000,7 +14000,7 @@ S: Odd Fixes
W: http://kernelnewbies.org/KernelJanitors
KERNEL NFSD, SUNRPC, AND LOCKD SERVERS
-M: Chuck Lever <chuck.lever@oracle.com>
+M: Chuck Lever <cel@kernel.org>
M: Jeff Layton <jlayton@kernel.org>
R: NeilBrown <neil@brown.name>
R: Olga Kornievskaia <okorniev@redhat.com>
diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile
index 808f0f2a7be1..3f9569d3ba0e 100644
--- a/fs/lockd/Makefile
+++ b/fs/lockd/Makefile
@@ -8,7 +8,8 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_LOCKD) += lockd.o
lockd-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
- svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o netlink.o
+ svcshare.o svcproc.o svcsubs.o mon.o trace.o netlink.o \
+ nlm3xdr_gen.o
lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o svc4proc.o nlm4xdr_gen.o
lockd-$(CONFIG_PROC_FS) += procfs.o
@@ -25,17 +26,27 @@ lockd-$(CONFIG_PROC_FS) += procfs.o
XDRGEN = ../../tools/net/sunrpc/xdrgen/xdrgen
-XDRGEN_DEFINITIONS = ../../include/linux/sunrpc/xdrgen/nlm4.h
-XDRGEN_DECLARATIONS = nlm4xdr_gen.h
-XDRGEN_SOURCE = nlm4xdr_gen.c
+XDRGEN_DEFINITIONS = ../../include/linux/sunrpc/xdrgen/nlm4.h \
+ ../../include/linux/sunrpc/xdrgen/nlm3.h
+XDRGEN_DECLARATIONS = nlm4xdr_gen.h nlm3xdr_gen.h
+XDRGEN_SOURCE = nlm4xdr_gen.c nlm3xdr_gen.c
xdrgen: $(XDRGEN_DEFINITIONS) $(XDRGEN_DECLARATIONS) $(XDRGEN_SOURCE)
../../include/linux/sunrpc/xdrgen/nlm4.h: ../../Documentation/sunrpc/xdr/nlm4.x
$(XDRGEN) definitions $< > $@
+../../include/linux/sunrpc/xdrgen/nlm3.h: ../../Documentation/sunrpc/xdr/nlm3.x
+ $(XDRGEN) definitions $< > $@
+
nlm4xdr_gen.h: ../../Documentation/sunrpc/xdr/nlm4.x
$(XDRGEN) declarations $< > $@
+nlm3xdr_gen.h: ../../Documentation/sunrpc/xdr/nlm3.x
+ $(XDRGEN) declarations $< > $@
+
nlm4xdr_gen.c: ../../Documentation/sunrpc/xdr/nlm4.x
$(XDRGEN) source --peer server $< > $@
+
+nlm3xdr_gen.c: ../../Documentation/sunrpc/xdr/nlm3.x
+ $(XDRGEN) source --peer server $< > $@
diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c
index 2058733eacf8..96a4a1e6a6b6 100644
--- a/fs/lockd/clnt4xdr.c
+++ b/fs/lockd/clnt4xdr.c
@@ -63,7 +63,7 @@ static s64 loff_t_to_s64(loff_t offset)
return res;
}
-static void nlm4_compute_offsets(const struct nlm_lock *lock,
+static void nlm4_compute_offsets(const struct lockd_lock *lock,
u64 *l_offset, u64 *l_len)
{
const struct file_lock *fl = &lock->fl;
@@ -132,13 +132,13 @@ static int decode_netobj(struct xdr_stream *xdr,
* netobj cookie;
*/
static void encode_cookie(struct xdr_stream *xdr,
- const struct nlm_cookie *cookie)
+ const struct lockd_cookie *cookie)
{
encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
}
static int decode_cookie(struct xdr_stream *xdr,
- struct nlm_cookie *cookie)
+ struct lockd_cookie *cookie)
{
u32 length;
__be32 *p;
@@ -238,9 +238,9 @@ out_overflow:
* };
*/
static void encode_nlm4_holder(struct xdr_stream *xdr,
- const struct nlm_res *result)
+ const struct lockd_res *result)
{
- const struct nlm_lock *lock = &result->lock;
+ const struct lockd_lock *lock = &result->lock;
u64 l_offset, l_len;
__be32 *p;
@@ -254,9 +254,9 @@ static void encode_nlm4_holder(struct xdr_stream *xdr,
xdr_encode_hyper(p, l_len);
}
-static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
+static int decode_nlm4_holder(struct xdr_stream *xdr, struct lockd_res *result)
{
- struct nlm_lock *lock = &result->lock;
+ struct lockd_lock *lock = &result->lock;
struct file_lock *fl = &lock->fl;
u64 l_offset, l_len;
u32 exclusive;
@@ -317,7 +317,7 @@ static void encode_caller_name(struct xdr_stream *xdr, const char *name)
* };
*/
static void encode_nlm4_lock(struct xdr_stream *xdr,
- const struct nlm_lock *lock)
+ const struct lockd_lock *lock)
{
u64 l_offset, l_len;
__be32 *p;
@@ -354,8 +354,8 @@ static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
@@ -376,8 +376,8 @@ static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
@@ -399,8 +399,8 @@ static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
@@ -418,8 +418,8 @@ static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_nlm4_lock(xdr, lock);
@@ -435,7 +435,7 @@ static void nlm4_xdr_enc_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_res *result = data;
+ const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm4_stat(xdr, result->status);
@@ -458,7 +458,7 @@ static void nlm4_xdr_enc_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_res *result = data;
+ const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm4_stat(xdr, result->status);
@@ -489,7 +489,7 @@ static void nlm4_xdr_enc_testres(struct rpc_rqst *req,
* };
*/
static int decode_nlm4_testrply(struct xdr_stream *xdr,
- struct nlm_res *result)
+ struct lockd_res *result)
{
int error;
@@ -506,7 +506,7 @@ static int nlm4_xdr_dec_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
- struct nlm_res *result = data;
+ struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
@@ -527,7 +527,7 @@ static int nlm4_xdr_dec_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
- struct nlm_res *result = data;
+ struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 8fa30c42c92a..f797cc99f94d 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -158,7 +158,7 @@ int nlmclnt_wait(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
/*
* The server lockd has called us back to tell us the lock was granted
*/
-__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
+__be32 nlmclnt_grant(const struct sockaddr *addr, const struct lockd_lock *lock)
{
const struct file_lock *fl = &lock->fl;
const struct nfs_fh *fh = &lock->fh;
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 7f211008a5d2..f06faf577cea 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -42,7 +42,7 @@ static const struct rpc_call_ops nlmclnt_cancel_ops;
*/
static atomic_t nlm_cookie = ATOMIC_INIT(0x1234);
-void nlmclnt_next_cookie(struct nlm_cookie *c)
+void nlmclnt_next_cookie(struct lockd_cookie *c)
{
u32 cookie = atomic_inc_return(&nlm_cookie);
@@ -128,8 +128,8 @@ static struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_ow
*/
static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
{
- struct nlm_args *argp = &req->a_args;
- struct nlm_lock *lock = &argp->lock;
+ struct lockd_args *argp = &req->a_args;
+ struct lockd_lock *lock = &argp->lock;
char *nodename = req->a_host->h_rpcclnt->cl_nodename;
nlmclnt_next_cookie(&argp->cookie);
@@ -266,8 +266,8 @@ nlmclnt_call(const struct cred *cred, struct nlm_rqst *req, u32 proc)
{
struct nlm_host *host = req->a_host;
struct rpc_clnt *clnt;
- struct nlm_args *argp = &req->a_args;
- struct nlm_res *resp = &req->a_res;
+ struct lockd_args *argp = &req->a_args;
+ struct lockd_res *resp = &req->a_res;
struct rpc_message msg = {
.rpc_argp = argp,
.rpc_resp = resp,
@@ -523,7 +523,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
{
const struct cred *cred = nfs_file_cred(fl->c.flc_file);
struct nlm_host *host = req->a_host;
- struct nlm_res *resp = &req->a_res;
+ struct lockd_res *resp = &req->a_res;
struct nlm_wait block;
unsigned char flags = fl->c.flc_flags;
unsigned char type;
@@ -686,7 +686,7 @@ static int
nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
{
struct nlm_host *host = req->a_host;
- struct nlm_res *resp = &req->a_res;
+ struct lockd_res *resp = &req->a_res;
int status;
unsigned char flags = fl->c.flc_flags;
diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c
index 65555f5224b1..3789ecb0b984 100644
--- a/fs/lockd/clntxdr.c
+++ b/fs/lockd/clntxdr.c
@@ -60,7 +60,7 @@ static s32 loff_t_to_s32(loff_t offset)
return res;
}
-static void nlm_compute_offsets(const struct nlm_lock *lock,
+static void nlm_compute_offsets(const struct lockd_lock *lock,
u32 *l_offset, u32 *l_len)
{
const struct file_lock *fl = &lock->fl;
@@ -130,13 +130,13 @@ static int decode_netobj(struct xdr_stream *xdr,
* netobj cookie;
*/
static void encode_cookie(struct xdr_stream *xdr,
- const struct nlm_cookie *cookie)
+ const struct lockd_cookie *cookie)
{
encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
}
static int decode_cookie(struct xdr_stream *xdr,
- struct nlm_cookie *cookie)
+ struct lockd_cookie *cookie)
{
u32 length;
__be32 *p;
@@ -234,9 +234,9 @@ out_overflow:
* };
*/
static void encode_nlm_holder(struct xdr_stream *xdr,
- const struct nlm_res *result)
+ const struct lockd_res *result)
{
- const struct nlm_lock *lock = &result->lock;
+ const struct lockd_lock *lock = &result->lock;
u32 l_offset, l_len;
__be32 *p;
@@ -250,9 +250,9 @@ static void encode_nlm_holder(struct xdr_stream *xdr,
*p = cpu_to_be32(l_len);
}
-static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result)
+static int decode_nlm_holder(struct xdr_stream *xdr, struct lockd_res *result)
{
- struct nlm_lock *lock = &result->lock;
+ struct lockd_lock *lock = &result->lock;
struct file_lock *fl = &lock->fl;
u32 exclusive, l_offset, l_len;
int error;
@@ -319,7 +319,7 @@ static void encode_caller_name(struct xdr_stream *xdr, const char *name)
* };
*/
static void encode_nlm_lock(struct xdr_stream *xdr,
- const struct nlm_lock *lock)
+ const struct lockd_lock *lock)
{
u32 l_offset, l_len;
__be32 *p;
@@ -355,8 +355,8 @@ static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
@@ -377,8 +377,8 @@ static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
@@ -400,8 +400,8 @@ static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
@@ -419,8 +419,8 @@ static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_args *args = data;
- const struct nlm_lock *lock = &args->lock;
+ const struct lockd_args *args = data;
+ const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_nlm_lock(xdr, lock);
@@ -436,7 +436,7 @@ static void nlm_xdr_enc_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_res *result = data;
+ const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm_stat(xdr, result->status);
@@ -456,7 +456,7 @@ static void nlm_xdr_enc_res(struct rpc_rqst *req,
* };
*/
static void encode_nlm_testrply(struct xdr_stream *xdr,
- const struct nlm_res *result)
+ const struct lockd_res *result)
{
if (result->status == nlm_lck_denied)
encode_nlm_holder(xdr, result);
@@ -466,7 +466,7 @@ static void nlm_xdr_enc_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
- const struct nlm_res *result = data;
+ const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm_stat(xdr, result->status);
@@ -495,7 +495,7 @@ static void nlm_xdr_enc_testres(struct rpc_rqst *req,
* };
*/
static int decode_nlm_testrply(struct xdr_stream *xdr,
- struct nlm_res *result)
+ struct lockd_res *result)
{
int error;
@@ -512,7 +512,7 @@ static int nlm_xdr_dec_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
- struct nlm_res *result = data;
+ struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
@@ -533,7 +533,7 @@ static int nlm_xdr_dec_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
- struct nlm_res *result = data;
+ struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index ea8a8e166f7e..d572cb27533f 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -552,7 +552,7 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
static struct nlm_host *next_host_state(struct hlist_head *cache,
struct nsm_handle *nsm,
- const struct nlm_reboot *info)
+ const struct lockd_reboot *info)
{
struct nlm_host *host;
struct hlist_head *chain;
@@ -582,7 +582,7 @@ static struct nlm_host *next_host_state(struct hlist_head *cache,
* We were notified that the specified host has rebooted. Release
* all resources held by that peer.
*/
-void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info)
+void nlm_host_rebooted(const struct net *net, const struct lockd_reboot *info)
{
struct nsm_handle *nsm;
struct nlm_host *host;
diff --git a/fs/lockd/lockd.h b/fs/lockd/lockd.h
index 1db6cb352542..e418a50c4180 100644
--- a/fs/lockd/lockd.h
+++ b/fs/lockd/lockd.h
@@ -52,6 +52,14 @@
*/
#define LOCKD_DFLT_TIMEO 10
+/*
+ * Number of leading bytes of nfs_fh.data that file_hash()
+ * digests when bucketing nlm_files[]. Sized for historical
+ * NFSv2 handles; nfs_fh.data must be initialized at least
+ * this far before lookup, regardless of fh.size.
+ */
+#define LOCKD_FH_HASH_SIZE 32
+
/* error codes new to NLMv4 */
#define nlm4_deadlock cpu_to_be32(NLM_DEADLCK)
#define nlm4_rofs cpu_to_be32(NLM_ROFS)
@@ -171,15 +179,15 @@ struct nlm_rqst {
refcount_t a_count;
unsigned int a_flags; /* initial RPC task flags */
struct nlm_host * a_host; /* host handle */
- struct nlm_args a_args; /* arguments */
- struct nlm_res a_res; /* result */
+ struct lockd_args a_args; /* arguments */
+ struct lockd_res a_res; /* result */
struct nlm_block * a_block;
unsigned int a_retries; /* Retry count */
u8 a_owner[NLMCLNT_OHSIZE];
void * a_callback_data; /* sent to nlmclnt_operations callbacks */
};
-struct nlm_share;
+struct lockd_share;
/*
* This struct describes a file held open by lockd on behalf of
@@ -190,7 +198,7 @@ struct nlm_file {
struct nfs_fh f_handle; /* NFS file handle */
struct file * f_file[2]; /* VFS file pointers,
indexed by O_ flags */
- struct nlm_share * f_shares; /* DOS shares */
+ struct lockd_share * f_shares; /* DOS shares */
struct list_head f_blocks; /* blocked locks */
unsigned int f_locks; /* guesstimate # of locks */
unsigned int f_count; /* reference count */
@@ -253,11 +261,11 @@ void nlmclnt_queue_block(struct nlm_wait *block);
__be32 nlmclnt_dequeue_block(struct nlm_wait *block);
int nlmclnt_wait(struct nlm_wait *block, struct nlm_rqst *req, long timeout);
__be32 nlmclnt_grant(const struct sockaddr *addr,
- const struct nlm_lock *lock);
+ const struct lockd_lock *lock);
void nlmclnt_recovery(struct nlm_host *);
int nlmclnt_reclaim(struct nlm_host *, struct file_lock *,
struct nlm_rqst *);
-void nlmclnt_next_cookie(struct nlm_cookie *);
+void nlmclnt_next_cookie(struct lockd_cookie *);
#ifdef CONFIG_LOCKD_V4
extern const struct rpc_version nlm_version4;
@@ -285,7 +293,7 @@ struct nlm_host * nlm_get_host(struct nlm_host *);
void nlm_shutdown_hosts(void);
void nlm_shutdown_hosts_net(struct net *net);
void nlm_host_rebooted(const struct net *net,
- const struct nlm_reboot *);
+ const struct lockd_reboot *);
/*
* Host monitoring
@@ -299,7 +307,7 @@ struct nsm_handle *nsm_get_handle(const struct net *net,
const char *hostname,
const size_t hostname_len);
struct nsm_handle *nsm_reboot_lookup(const struct net *net,
- const struct nlm_reboot *info);
+ const struct lockd_reboot *info);
void nsm_release(struct nsm_handle *nsm);
/*
@@ -313,17 +321,17 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
*/
int lock_to_openmode(struct file_lock *);
__be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
- struct nlm_host *, struct nlm_lock *, int,
- struct nlm_cookie *, int);
-__be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct nlm_lock *);
+ struct nlm_host *, struct lockd_lock *, int,
+ struct lockd_cookie *, int);
+__be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct lockd_lock *);
__be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
- struct nlm_host *host, struct nlm_lock *lock,
- struct nlm_lock *conflock);
-__be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *);
+ struct nlm_host *host, struct lockd_lock *lock,
+ struct lockd_lock *conflock);
+__be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct lockd_lock *);
void nlmsvc_retry_blocked(struct svc_rqst *rqstp);
void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
nlm_host_match_fn_t match);
-void nlmsvc_grant_reply(struct nlm_cookie *, __be32);
+void nlmsvc_grant_reply(struct lockd_cookie *, __be32);
void nlmsvc_release_call(struct nlm_rqst *);
void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t);
int nlmsvc_dispatch(struct svc_rqst *rqstp);
@@ -332,10 +340,10 @@ int nlmsvc_dispatch(struct svc_rqst *rqstp);
* File handling for the server personality
*/
__be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
- struct nlm_lock *, int);
+ struct lockd_lock *, int);
void nlm_release_file(struct nlm_file *);
void nlmsvc_put_lockowner(struct nlm_lockowner *);
-void nlmsvc_release_lockowner(struct nlm_lock *);
+void nlmsvc_release_lockowner(struct lockd_lock *);
void nlmsvc_mark_resources(struct net *);
void nlmsvc_free_host_resources(struct nlm_host *);
void nlmsvc_invalidate_all(void);
@@ -423,6 +431,29 @@ static inline int nlm_compare_locks(const struct file_lock *fl1,
}
/**
+ * lockd_set_file_lock_range3 - set the byte range of a file_lock
+ * @fl: file_lock whose length fields are to be initialized
+ * @off: starting offset of the lock, in bytes
+ * @len: length of the byte range, in bytes, or zero
+ *
+ * NLMv3 uses a (start, length) representation for lock byte ranges,
+ * while the kernel's file_lock uses (start, end). Treat a length of
+ * zero or arithmetic overflow (end wrapping negative when the sum
+ * exceeds S32_MAX) as "lock to end of file."
+ */
+static inline void
+lockd_set_file_lock_range3(struct file_lock *fl, u32 off, u32 len)
+{
+ s32 end = off + len - 1;
+
+ fl->fl_start = off;
+ if (len == 0 || end < 0)
+ fl->fl_end = OFFSET_MAX;
+ else
+ fl->fl_end = end;
+}
+
+/**
* lockd_set_file_lock_range4 - set the byte range of a file_lock
* @fl: file_lock whose length fields are to be initialized
* @off: starting offset of the lock, in bytes
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 3d3ee88ca4dc..a8f5ac6f0577 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -377,7 +377,7 @@ retry:
* error occurred.
*/
struct nsm_handle *nsm_reboot_lookup(const struct net *net,
- const struct nlm_reboot *info)
+ const struct lockd_reboot *info)
{
struct nsm_handle *cached;
struct lockd_net *ln = net_generic(net, lockd_net_id);
diff --git a/fs/lockd/nlm3xdr_gen.c b/fs/lockd/nlm3xdr_gen.c
new file mode 100644
index 000000000000..9ed5a41b5daf
--- /dev/null
+++ b/fs/lockd/nlm3xdr_gen.c
@@ -0,0 +1,714 @@
+// 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
+
+#include <linux/sunrpc/svc.h>
+
+#include "nlm3xdr_gen.h"
+
+static bool __maybe_unused
+xdrgen_decode_netobj(struct xdr_stream *xdr, netobj *ptr)
+{
+ return xdrgen_decode_opaque(xdr, ptr, MAXNETOBJ_SZ);
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_stats(struct xdr_stream *xdr, nlm_stats *ptr)
+{
+ __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 LCK_GRANTED:
+ case LCK_DENIED:
+ case LCK_DENIED_NOLOCKS:
+ case LCK_BLOCKED:
+ case LCK_DENIED_GRACE_PERIOD:
+ break;
+ default:
+ return false;
+ }
+ *ptr = raw;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_stat(struct xdr_stream *xdr, struct nlm_stat *ptr)
+{
+ if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_res(struct xdr_stream *xdr, struct nlm_res *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_nlm_stat(xdr, &ptr->stat))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_holder(struct xdr_stream *xdr, struct nlm_holder *ptr)
+{
+ if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
+ return false;
+ if (!xdrgen_decode_int(xdr, &ptr->uppid))
+ return false;
+ if (!xdrgen_decode_netobj(xdr, &ptr->oh))
+ return false;
+ if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_offset))
+ return false;
+ if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_len))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_testrply(struct xdr_stream *xdr, struct nlm_testrply *ptr)
+{
+ if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat))
+ return false;
+ switch (ptr->stat) {
+ case __constant_cpu_to_be32(LCK_DENIED):
+ if (!xdrgen_decode_nlm_holder(xdr, &ptr->u.holder))
+ return false;
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_testres(struct xdr_stream *xdr, struct nlm_testres *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_nlm_testrply(xdr, &ptr->test_stat))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_lock(struct xdr_stream *xdr, struct nlm_lock *ptr)
+{
+ if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN))
+ return false;
+ if (!xdrgen_decode_netobj(xdr, &ptr->fh))
+ return false;
+ if (!xdrgen_decode_netobj(xdr, &ptr->oh))
+ return false;
+ if (!xdrgen_decode_int(xdr, &ptr->uppid))
+ return false;
+ if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_offset))
+ return false;
+ if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_len))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_lockargs(struct xdr_stream *xdr, struct nlm_lockargs *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->block))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
+ return false;
+ if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->reclaim))
+ return false;
+ if (!xdrgen_decode_int(xdr, &ptr->state))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_cancargs(struct xdr_stream *xdr, struct nlm_cancargs *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->block))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
+ return false;
+ if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_testargs(struct xdr_stream *xdr, struct nlm_testargs *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
+ return false;
+ if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_unlockargs(struct xdr_stream *xdr, struct nlm_unlockargs *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_fsh_mode(struct xdr_stream *xdr, fsh_mode *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 fsm_DN:
+ case fsm_DR:
+ case fsm_DW:
+ case fsm_DRW:
+ break;
+ default:
+ return false;
+ }
+ *ptr = val;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_fsh_access(struct xdr_stream *xdr, fsh_access *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 fsa_NONE:
+ case fsa_R:
+ case fsa_W:
+ case fsa_RW:
+ break;
+ default:
+ return false;
+ }
+ *ptr = val;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_share(struct xdr_stream *xdr, struct nlm_share *ptr)
+{
+ if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN))
+ return false;
+ if (!xdrgen_decode_netobj(xdr, &ptr->fh))
+ return false;
+ if (!xdrgen_decode_netobj(xdr, &ptr->oh))
+ return false;
+ if (!xdrgen_decode_fsh_mode(xdr, &ptr->mode))
+ return false;
+ if (!xdrgen_decode_fsh_access(xdr, &ptr->access))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_shareargs(struct xdr_stream *xdr, struct nlm_shareargs *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_nlm_share(xdr, &ptr->share))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->reclaim))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_shareres(struct xdr_stream *xdr, struct nlm_shareres *ptr)
+{
+ if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
+ return false;
+ if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat))
+ return false;
+ if (!xdrgen_decode_int(xdr, &ptr->sequence))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_notify(struct xdr_stream *xdr, struct nlm_notify *ptr)
+{
+ if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXNAMELEN))
+ return false;
+ if (!xdrgen_decode_long(xdr, &ptr->state))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_nlm_notifyargs(struct xdr_stream *xdr, struct nlm_notifyargs *ptr)
+{
+ if (!xdrgen_decode_nlm_notify(xdr, &ptr->notify))
+ return false;
+ if (xdr_stream_decode_opaque_fixed(xdr, ptr->private, SM_PRIV_SIZE) < 0)
+ return false;
+ 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
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_testargs *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_testargs(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_lockargs - Decode a nlm_lockargs argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_lockargs *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_lockargs(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_cancargs - Decode a nlm_cancargs argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_cancargs *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_cancargs(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_unlockargs - Decode a nlm_unlockargs argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_unlockargs *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_unlockargs(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_testres - Decode a nlm_testres argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_testres *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_testres(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_res - Decode a nlm_res argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_res *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_res(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_notifyargs - Decode a nlm_notifyargs argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_notifyargs *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_notifyargs(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_shareargs - Decode a nlm_shareargs argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_shareargs *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_shareargs(xdr, argp);
+}
+
+/**
+ * nlm_svc_decode_nlm_notify - Decode a nlm_notify argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool nlm_svc_decode_nlm_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_notify *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_nlm_notify(xdr, argp);
+}
+
+static bool __maybe_unused
+xdrgen_encode_netobj(struct xdr_stream *xdr, const netobj value)
+{
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_stats(struct xdr_stream *xdr, nlm_stats value)
+{
+ return xdr_stream_encode_be32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_stat(struct xdr_stream *xdr, const struct nlm_stat *value)
+{
+ if (!xdrgen_encode_nlm_stats(xdr, value->stat))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_res(struct xdr_stream *xdr, const struct nlm_res *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_nlm_stat(xdr, &value->stat))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_holder(struct xdr_stream *xdr, const struct nlm_holder *value)
+{
+ if (!xdrgen_encode_bool(xdr, value->exclusive))
+ return false;
+ if (!xdrgen_encode_int(xdr, value->uppid))
+ return false;
+ if (!xdrgen_encode_netobj(xdr, value->oh))
+ return false;
+ if (!xdrgen_encode_unsigned_int(xdr, value->l_offset))
+ return false;
+ if (!xdrgen_encode_unsigned_int(xdr, value->l_len))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_testrply(struct xdr_stream *xdr, const struct nlm_testrply *ptr)
+{
+ if (!xdrgen_encode_nlm_stats(xdr, ptr->stat))
+ return false;
+ switch (ptr->stat) {
+ case __constant_cpu_to_be32(LCK_DENIED):
+ if (!xdrgen_encode_nlm_holder(xdr, &ptr->u.holder))
+ return false;
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_testres(struct xdr_stream *xdr, const struct nlm_testres *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_nlm_testrply(xdr, &value->test_stat))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_lock(struct xdr_stream *xdr, const struct nlm_lock *value)
+{
+ if (value->caller_name.len > LM_MAXSTRLEN)
+ return false;
+ if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0)
+ return false;
+ if (!xdrgen_encode_netobj(xdr, value->fh))
+ return false;
+ if (!xdrgen_encode_netobj(xdr, value->oh))
+ return false;
+ if (!xdrgen_encode_int(xdr, value->uppid))
+ return false;
+ if (!xdrgen_encode_unsigned_int(xdr, value->l_offset))
+ return false;
+ if (!xdrgen_encode_unsigned_int(xdr, value->l_len))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_lockargs(struct xdr_stream *xdr, const struct nlm_lockargs *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->block))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->exclusive))
+ return false;
+ if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->reclaim))
+ return false;
+ if (!xdrgen_encode_int(xdr, value->state))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_cancargs(struct xdr_stream *xdr, const struct nlm_cancargs *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->block))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->exclusive))
+ return false;
+ if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_testargs(struct xdr_stream *xdr, const struct nlm_testargs *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->exclusive))
+ return false;
+ if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_unlockargs(struct xdr_stream *xdr, const struct nlm_unlockargs *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_fsh_mode(struct xdr_stream *xdr, fsh_mode value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_fsh_access(struct xdr_stream *xdr, fsh_access value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_share(struct xdr_stream *xdr, const struct nlm_share *value)
+{
+ if (value->caller_name.len > LM_MAXSTRLEN)
+ return false;
+ if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0)
+ return false;
+ if (!xdrgen_encode_netobj(xdr, value->fh))
+ return false;
+ if (!xdrgen_encode_netobj(xdr, value->oh))
+ return false;
+ if (!xdrgen_encode_fsh_mode(xdr, value->mode))
+ return false;
+ if (!xdrgen_encode_fsh_access(xdr, value->access))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_shareargs(struct xdr_stream *xdr, const struct nlm_shareargs *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_nlm_share(xdr, &value->share))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->reclaim))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_shareres(struct xdr_stream *xdr, const struct nlm_shareres *value)
+{
+ if (!xdrgen_encode_netobj(xdr, value->cookie))
+ return false;
+ if (!xdrgen_encode_nlm_stats(xdr, value->stat))
+ return false;
+ if (!xdrgen_encode_int(xdr, value->sequence))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_notify(struct xdr_stream *xdr, const struct nlm_notify *value)
+{
+ if (value->name.len > LM_MAXNAMELEN)
+ return false;
+ if (xdr_stream_encode_opaque(xdr, value->name.data, value->name.len) < 0)
+ return false;
+ if (!xdrgen_encode_long(xdr, value->state))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_nlm_notifyargs(struct xdr_stream *xdr, const struct nlm_notifyargs *value)
+{
+ if (!xdrgen_encode_nlm_notify(xdr, &value->notify))
+ return false;
+ if (xdr_stream_encode_opaque_fixed(xdr, value->private, SM_PRIV_SIZE) < 0)
+ return false;
+ 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
+ * @xdr: target XDR data stream
+ *
+ * Return values:
+ * %true: procedure results encoded successfully
+ * %false: encode failed
+ */
+bool nlm_svc_encode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_testres *resp = rqstp->rq_resp;
+
+ return xdrgen_encode_nlm_testres(xdr, resp);
+}
+
+/**
+ * nlm_svc_encode_nlm_res - Encode a nlm_res result
+ * @rqstp: RPC transaction context
+ * @xdr: target XDR data stream
+ *
+ * Return values:
+ * %true: procedure results encoded successfully
+ * %false: encode failed
+ */
+bool nlm_svc_encode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_res *resp = rqstp->rq_resp;
+
+ return xdrgen_encode_nlm_res(xdr, resp);
+}
+
+/**
+ * nlm_svc_encode_nlm_shareres - Encode a nlm_shareres result
+ * @rqstp: RPC transaction context
+ * @xdr: target XDR data stream
+ *
+ * Return values:
+ * %true: procedure results encoded successfully
+ * %false: encode failed
+ */
+bool nlm_svc_encode_nlm_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+ struct nlm_shareres *resp = rqstp->rq_resp;
+
+ return xdrgen_encode_nlm_shareres(xdr, resp);
+}
diff --git a/fs/lockd/nlm3xdr_gen.h b/fs/lockd/nlm3xdr_gen.h
new file mode 100644
index 000000000000..c99038e99805
--- /dev/null
+++ b/fs/lockd/nlm3xdr_gen.h
@@ -0,0 +1,32 @@
+/* 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 */
+
+#ifndef _LINUX_XDRGEN_NLM3_DECL_H
+#define _LINUX_XDRGEN_NLM3_DECL_H
+
+#include <linux/types.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/xdrgen/_defs.h>
+#include <linux/sunrpc/xdrgen/_builtins.h>
+#include <linux/sunrpc/xdrgen/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);
+bool nlm_svc_decode_nlm_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
+bool nlm_svc_decode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
+bool nlm_svc_decode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
+bool nlm_svc_decode_nlm_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
+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);
+
+#endif /* _LINUX_XDRGEN_NLM3_DECL_H */
diff --git a/fs/lockd/share.h b/fs/lockd/share.h
index 20ea8ee49168..1ec3ccdb2aef 100644
--- a/fs/lockd/share.h
+++ b/fs/lockd/share.h
@@ -14,8 +14,8 @@
/*
* DOS share for a specific file
*/
-struct nlm_share {
- struct nlm_share * s_next; /* linked list */
+struct lockd_share {
+ struct lockd_share * s_next; /* linked list */
struct nlm_host * s_host; /* client host */
struct nlm_file * s_file; /* shared file */
struct xdr_netobj s_owner; /* owner handle */
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 41cab858de57..78e675470c4b 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -26,42 +26,42 @@
#include "nlm4xdr_gen.h"
/*
- * Wrapper structures combine xdrgen types with legacy nlm_lock.
+ * Wrapper structures combine xdrgen types with legacy lockd_lock.
* The xdrgen field must be first so the structure can be cast
* to its XDR type for the RPC dispatch layer.
*/
struct nlm4_testargs_wrapper {
struct nlm4_testargs xdrgen;
- struct nlm_lock lock;
+ struct lockd_lock lock;
};
static_assert(offsetof(struct nlm4_testargs_wrapper, xdrgen) == 0);
struct nlm4_lockargs_wrapper {
struct nlm4_lockargs xdrgen;
- struct nlm_cookie cookie;
- struct nlm_lock lock;
+ struct lockd_cookie cookie;
+ struct lockd_lock lock;
};
static_assert(offsetof(struct nlm4_lockargs_wrapper, xdrgen) == 0);
struct nlm4_cancargs_wrapper {
struct nlm4_cancargs xdrgen;
- struct nlm_lock lock;
+ struct lockd_lock lock;
};
static_assert(offsetof(struct nlm4_cancargs_wrapper, xdrgen) == 0);
struct nlm4_unlockargs_wrapper {
struct nlm4_unlockargs xdrgen;
- struct nlm_lock lock;
+ struct lockd_lock lock;
};
static_assert(offsetof(struct nlm4_unlockargs_wrapper, xdrgen) == 0);
struct nlm4_notifyargs_wrapper {
struct nlm4_notifyargs xdrgen;
- struct nlm_reboot reboot;
+ struct lockd_reboot reboot;
};
static_assert(offsetof(struct nlm4_notifyargs_wrapper, xdrgen) == 0);
@@ -74,12 +74,12 @@ static_assert(offsetof(struct nlm4_notify_wrapper, xdrgen) == 0);
struct nlm4_testres_wrapper {
struct nlm4_testres xdrgen;
- struct nlm_lock lock;
+ struct lockd_lock lock;
};
struct nlm4_shareargs_wrapper {
struct nlm4_shareargs xdrgen;
- struct nlm_lock lock;
+ struct lockd_lock lock;
};
static_assert(offsetof(struct nlm4_shareargs_wrapper, xdrgen) == 0);
@@ -88,7 +88,7 @@ static_assert(offsetof(struct nlm4_testres_wrapper, xdrgen) == 0);
struct nlm4_res_wrapper {
struct nlm4_res xdrgen;
- struct nlm_cookie cookie;
+ struct lockd_cookie cookie;
};
static_assert(offsetof(struct nlm4_res_wrapper, xdrgen) == 0);
@@ -100,7 +100,7 @@ struct nlm4_shareres_wrapper {
static_assert(offsetof(struct nlm4_shareres_wrapper, xdrgen) == 0);
static __be32
-nlm4_netobj_to_cookie(struct nlm_cookie *cookie, netobj *object)
+nlm4_netobj_to_cookie(struct lockd_cookie *cookie, netobj *object)
{
if (object->len > NLM_MAXCOOKIELEN)
return nlm_lck_denied_nolocks;
@@ -110,7 +110,7 @@ nlm4_netobj_to_cookie(struct nlm_cookie *cookie, netobj *object)
}
static __be32
-nlm4_lock_to_nlm_lock(struct nlm_lock *lock, struct nlm4_lock *alock)
+nlm4_lock_to_lockd_lock(struct lockd_lock *lock, struct nlm4_lock *alock)
{
if (alock->fh.len > NFS_MAXFHSIZE)
return nlm_lck_denied;
@@ -119,7 +119,6 @@ nlm4_lock_to_nlm_lock(struct nlm_lock *lock, struct nlm4_lock *alock)
lock->oh.len = alock->oh.len;
lock->oh.data = alock->oh.data;
lock->svid = alock->svid;
- locks_init_lock(&lock->fl);
lockd_set_file_lock_range4(&lock->fl, alock->l_offset, alock->l_len);
return nlm_granted;
}
@@ -143,7 +142,7 @@ nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
static __be32
nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
- struct nlm_lock *lock, struct nlm_file **filp,
+ struct lockd_lock *lock, struct nlm_file **filp,
struct nlm4_lock *xdr_lock, unsigned char type)
{
bool is_test = (rqstp->rq_proc == NLMPROC4_TEST ||
@@ -157,6 +156,9 @@ nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
return nlm_lck_denied_nolocks;
lock->fh.size = xdr_lock->fh.len;
memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len);
+ if (xdr_lock->fh.len < LOCKD_FH_HASH_SIZE)
+ memset(lock->fh.data + xdr_lock->fh.len, 0,
+ LOCKD_FH_HASH_SIZE - xdr_lock->fh.len);
lock->oh.len = xdr_lock->oh.len;
lock->oh.data = xdr_lock->oh.data;
@@ -270,7 +272,7 @@ static __be32 nlm4svc_proc_test(struct svc_rqst *rqstp)
nlmsvc_release_lockowner(&argp->lock);
if (resp->xdrgen.stat.stat == nlm_lck_denied) {
- struct nlm_lock *conf = &resp->lock;
+ struct lockd_lock *conf = &resp->lock;
struct nlm4_holder *holder = &resp->xdrgen.stat.u.holder;
holder->exclusive = (conf->fl.c.flc_type != F_RDLCK);
@@ -513,12 +515,12 @@ out:
* nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5;
*
* Permissible procedure status codes:
- * %NLM4_GRANTED: The requested lock was granted.
- * %NLM4_DENIED: The server could not allocate the resources
- * needed to process the request.
- * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
- * re-establishing existing locks, and is not
- * yet ready to accept normal service requests.
+ * %NLM4_GRANTED: The granted lock was accepted.
+ * %NLM4_DENIED: The procedure failed, possibly due to
+ * internal resource constraints.
+ * %NLM4_DENIED_GRACE_PERIOD: The client host recently restarted and
+ * its NLM is re-establishing existing locks,
+ * so it is not yet ready to accept callbacks.
*/
static __be32
nlm4svc_proc_granted(struct svc_rqst *rqstp)
@@ -528,8 +530,8 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp)
resp->xdrgen.cookie = argp->xdrgen.cookie;
- resp->xdrgen.stat.stat = nlm4_lock_to_nlm_lock(&argp->lock,
- &argp->xdrgen.alock);
+ resp->xdrgen.stat.stat = nlm4_lock_to_lockd_lock(&argp->lock,
+ &argp->xdrgen.alock);
if (resp->xdrgen.stat.stat)
goto out;
@@ -563,7 +565,7 @@ static const struct rpc_call_ops nlm4svc_callback_ops = {
*/
static __be32
nlm4svc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc,
- __be32 (*func)(struct svc_rqst *, struct nlm_res *))
+ __be32 (*func)(struct svc_rqst *, struct lockd_res *))
{
struct nlm_rqst *call;
__be32 stat;
@@ -586,7 +588,7 @@ nlm4svc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc,
}
static __be32
-__nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
+__nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
@@ -646,7 +648,7 @@ static __be32 nlm4svc_proc_test_msg(struct svc_rqst *rqstp)
}
static __be32
-__nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
+__nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
@@ -669,6 +671,8 @@ __nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
argp->xdrgen.block, &resp->cookie,
argp->xdrgen.reclaim);
+ if (resp->status == nlm__int__deadlock)
+ resp->status = nlm4_deadlock;
nlmsvc_release_lockowner(&argp->lock);
out:
@@ -697,7 +701,7 @@ static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp)
struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
- host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true);
+ host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
return rpc_system_err;
@@ -706,7 +710,7 @@ static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp)
}
static __be32
-__nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
+__nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
@@ -770,7 +774,7 @@ static __be32 nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp)
}
static __be32
-__nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
+__nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
struct net *net = SVC_NET(rqstp);
@@ -833,7 +837,7 @@ static __be32 nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp)
}
static __be32
-__nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
+__nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
@@ -841,7 +845,7 @@ __nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
goto out;
- if (nlm4_lock_to_nlm_lock(&argp->lock, &argp->xdrgen.alock))
+ if (nlm4_lock_to_lockd_lock(&argp->lock, &argp->xdrgen.alock))
goto out;
resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
@@ -917,7 +921,7 @@ static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
static __be32 nlm4svc_proc_sm_notify(struct svc_rqst *rqstp)
{
struct nlm4_notifyargs_wrapper *argp = rqstp->rq_argp;
- struct nlm_reboot *reboot = &argp->reboot;
+ struct lockd_reboot *reboot = &argp->reboot;
if (!nlm_privileged_requester(rqstp)) {
char buf[RPC_MAX_ADDRBUFLEN];
@@ -981,7 +985,7 @@ static __be32 nlm4svc_proc_share(struct svc_rqst *rqstp)
{
struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp;
struct nlm4_shareres_wrapper *resp = rqstp->rq_resp;
- struct nlm_lock *lock = &argp->lock;
+ struct lockd_lock *lock = &argp->lock;
struct nlm_host *host = NULL;
struct nlm_file *file = NULL;
struct nlm4_lock xdr_lock = {
@@ -1049,7 +1053,7 @@ static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp)
{
struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp;
struct nlm4_shareres_wrapper *resp = rqstp->rq_resp;
- struct nlm_lock *lock = &argp->lock;
+ struct lockd_lock *lock = &argp->lock;
struct nlm4_lock xdr_lock = {
.fh = argp->xdrgen.share.fh,
.oh = argp->xdrgen.share.oh,
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index f4520149d6d7..e48d31f14a65 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -37,7 +37,7 @@ static void nlmsvc_release_block(struct nlm_block *block);
static void nlmsvc_insert_block(struct nlm_block *block, unsigned long);
static void nlmsvc_remove_block(struct nlm_block *block);
-static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
+static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct lockd_lock *lock);
static void nlmsvc_freegrantargs(struct nlm_rqst *call);
static const struct rpc_call_ops nlmsvc_grant_ops;
@@ -48,7 +48,7 @@ static LIST_HEAD(nlm_blocked);
static DEFINE_SPINLOCK(nlm_blocked_lock);
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
-static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
+static const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie)
{
/*
* We can get away with a static buffer because this is only called
@@ -75,7 +75,7 @@ static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
return buf;
}
#else
-static inline const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
+static inline const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie)
{
return "???";
}
@@ -142,7 +142,7 @@ nlmsvc_remove_block(struct nlm_block *block)
* Find a block for a given lock
*/
static struct nlm_block *
-nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
+nlmsvc_lookup_block(struct nlm_file *file, struct lockd_lock *lock)
{
struct nlm_block *block;
struct file_lock *fl;
@@ -171,7 +171,7 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
return NULL;
}
-static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
+static int lockd_cookie_match(struct lockd_cookie *a, struct lockd_cookie *b)
{
if (a->len != b->len)
return 0;
@@ -184,13 +184,13 @@ static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
* Find a block with a given NLM cookie.
*/
static inline struct nlm_block *
-nlmsvc_find_block(struct nlm_cookie *cookie)
+nlmsvc_find_block(struct lockd_cookie *cookie)
{
struct nlm_block *block;
spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
- if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie))
+ if (lockd_cookie_match(&block->b_call->a_args.cookie, cookie))
goto found;
}
spin_unlock(&nlm_blocked_lock);
@@ -221,8 +221,8 @@ found:
*/
static struct nlm_block *
nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
- struct nlm_file *file, struct nlm_lock *lock,
- struct nlm_cookie *cookie)
+ struct nlm_file *file, struct lockd_lock *lock,
+ struct lockd_cookie *cookie)
{
struct nlm_block *block;
struct nlm_rqst *call = NULL;
@@ -399,7 +399,7 @@ static struct nlm_lockowner *nlmsvc_find_lockowner(struct nlm_host *host, pid_t
}
void
-nlmsvc_release_lockowner(struct nlm_lock *lock)
+nlmsvc_release_lockowner(struct lockd_lock *lock)
{
if (lock->fl.c.flc_owner)
nlmsvc_put_lockowner(lock->fl.c.flc_owner);
@@ -415,7 +415,7 @@ void nlmsvc_locks_init_private(struct file_lock *fl, struct nlm_host *host,
* Initialize arguments for GRANTED call. The nlm_rqst structure
* has been cleared already.
*/
-static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
+static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct lockd_lock *lock)
{
locks_copy_lock(&call->a_args.lock.fl, &lock->fl);
memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh));
@@ -476,8 +476,8 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
*/
__be32
nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
- struct nlm_host *host, struct nlm_lock *lock, int wait,
- struct nlm_cookie *cookie, int reclaim)
+ struct nlm_host *host, struct lockd_lock *lock, int wait,
+ struct lockd_cookie *cookie, int reclaim)
{
struct inode *inode __maybe_unused = nlmsvc_file_inode(file);
struct nlm_block *block = NULL;
@@ -609,8 +609,8 @@ out:
*/
__be32
nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
- struct nlm_host *host, struct nlm_lock *lock,
- struct nlm_lock *conflock)
+ struct nlm_host *host, struct lockd_lock *lock,
+ struct lockd_lock *conflock)
{
int error;
__be32 ret;
@@ -669,7 +669,7 @@ out:
* must be removed.
*/
__be32
-nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
+nlmsvc_unlock(struct net *net, struct nlm_file *file, struct lockd_lock *lock)
{
int error = 0;
@@ -707,7 +707,7 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
* The calling procedure must check whether the file can be closed.
*/
__be32
-nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
+nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct lockd_lock *lock)
{
struct nlm_block *block;
int status = 0;
@@ -848,7 +848,7 @@ static void
nlmsvc_grant_blocked(struct nlm_block *block)
{
struct nlm_file *file = block->b_file;
- struct nlm_lock *lock = &block->b_call->a_args.lock;
+ struct lockd_lock *lock = &block->b_call->a_args.lock;
int mode;
int error;
loff_t fl_start, fl_end;
@@ -982,7 +982,7 @@ static const struct rpc_call_ops nlmsvc_grant_ops = {
* block.
*/
void
-nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status)
+nlmsvc_grant_reply(struct lockd_cookie *cookie, __be32 status)
{
struct nlm_block *block;
struct file_lock *fl;
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index c0a3487719e2..4836887f11ef 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -13,303 +13,534 @@
#include <linux/sunrpc/svc_xprt.h>
#include "lockd.h"
+
+/*
+ * xdr.h defines SM_PRIV_SIZE as a macro. nlm3xdr_gen.h defines
+ * it as an enum constant. Undefine the macro before including
+ * the generated header.
+ */
+#undef SM_PRIV_SIZE
+
#include "share.h"
+#include "nlm3xdr_gen.h"
+
+/*
+ * Size of an NFSv2 file handle, in bytes, which is 32.
+ * Defined locally to avoid including uapi/linux/nfs2.h.
+ */
+#define NLM3_FHSIZE 32
+
+/*
+ * Wrapper structures combine xdrgen types with legacy lockd_lock.
+ * The xdrgen field must be first so the structure can be cast
+ * to its XDR type for the RPC dispatch layer.
+ */
+struct nlm_testargs_wrapper {
+ struct nlm_testargs xdrgen;
+ struct lockd_lock lock;
+};
-#define NLMDBG_FACILITY NLMDBG_CLIENT
+static_assert(offsetof(struct nlm_testargs_wrapper, xdrgen) == 0);
-#ifdef CONFIG_LOCKD_V4
-static inline __be32 cast_status(__be32 status)
+struct nlm_testres_wrapper {
+ struct nlm_testres xdrgen;
+ struct lockd_lock lock;
+};
+
+static_assert(offsetof(struct nlm_testres_wrapper, xdrgen) == 0);
+
+struct nlm_lockargs_wrapper {
+ struct nlm_lockargs xdrgen;
+ struct lockd_cookie cookie;
+ struct lockd_lock lock;
+};
+
+static_assert(offsetof(struct nlm_lockargs_wrapper, xdrgen) == 0);
+
+struct nlm_res_wrapper {
+ struct nlm_res xdrgen;
+ struct lockd_cookie cookie;
+};
+
+static_assert(offsetof(struct nlm_res_wrapper, xdrgen) == 0);
+
+struct nlm_cancargs_wrapper {
+ struct nlm_cancargs xdrgen;
+ struct lockd_lock lock;
+};
+
+static_assert(offsetof(struct nlm_cancargs_wrapper, xdrgen) == 0);
+
+struct nlm_unlockargs_wrapper {
+ struct nlm_unlockargs xdrgen;
+ struct lockd_lock lock;
+};
+
+static_assert(offsetof(struct nlm_unlockargs_wrapper, xdrgen) == 0);
+
+struct nlm_notifyargs_wrapper {
+ struct nlm_notifyargs xdrgen;
+ struct lockd_reboot reboot;
+};
+
+static_assert(offsetof(struct nlm_notifyargs_wrapper, xdrgen) == 0);
+
+struct nlm_shareargs_wrapper {
+ struct nlm_shareargs xdrgen;
+ struct lockd_lock lock;
+};
+
+static_assert(offsetof(struct nlm_shareargs_wrapper, xdrgen) == 0);
+
+struct nlm_shareres_wrapper {
+ struct nlm_shareres xdrgen;
+};
+
+static_assert(offsetof(struct nlm_shareres_wrapper, xdrgen) == 0);
+
+struct nlm_notify_wrapper {
+ struct nlm_notify xdrgen;
+};
+
+static_assert(offsetof(struct nlm_notify_wrapper, xdrgen) == 0);
+
+static __be32
+nlm_netobj_to_cookie(struct lockd_cookie *cookie, netobj *object)
{
- switch (status) {
- case nlm_granted:
- case nlm_lck_denied:
- case nlm_lck_denied_nolocks:
- case nlm_lck_blocked:
- case nlm_lck_denied_grace_period:
- case nlm__int__drop_reply:
- break;
- case nlm__int__deadlock:
- status = nlm_lck_denied;
- break;
- default:
- status = nlm_lck_denied_nolocks;
- }
+ if (object->len > NLM_MAXCOOKIELEN)
+ return nlm_lck_denied_nolocks;
+ cookie->len = object->len;
+ memcpy(cookie->data, object->data, object->len);
+ return nlm_granted;
+}
- return status;
+static __be32
+nlm_lock_to_lockd_lock(struct lockd_lock *lock, struct nlm_lock *alock)
+{
+ if (alock->fh.len != NLM3_FHSIZE)
+ return nlm_lck_denied;
+ lock->fh.size = alock->fh.len;
+ memcpy(lock->fh.data, alock->fh.data, alock->fh.len);
+ lock->oh.len = alock->oh.len;
+ lock->oh.data = alock->oh.data;
+ lock->svid = alock->uppid;
+ lockd_set_file_lock_range3(&lock->fl, alock->l_offset, alock->l_len);
+ return nlm_granted;
}
-#else
-static inline __be32 cast_status(__be32 status)
+
+static struct nlm_host *
+nlm3svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
- switch (status) {
- case nlm__int__deadlock:
- status = nlm_lck_denied;
- break;
- case nlm__int__stale_fh:
- case nlm__int__failed:
- status = nlm_lck_denied_nolocks;
- break;
- default:
- if (be32_to_cpu(status) >= 30000)
- pr_warn_once("lockd: unhandled internal status %u\n",
- be32_to_cpu(status));
- break;
+ struct nlm_host *host;
+
+ if (!nlmsvc_ops)
+ return NULL;
+ host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
+ if (!host)
+ return NULL;
+ if (monitored && nsm_monitor(host) < 0) {
+ nlmsvc_release_host(host);
+ return NULL;
}
- return status;
+ return host;
}
-#endif
-/*
- * Obtain client and file from arguments
- */
static __be32
-nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
- struct nlm_host **hostp, struct nlm_file **filp)
+nlm3svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
+ struct lockd_lock *lock, struct nlm_file **filp,
+ struct nlm_lock *xdr_lock, unsigned char type)
{
- struct nlm_host *host = NULL;
- struct nlm_file *file = NULL;
- struct nlm_lock *lock = &argp->lock;
- bool is_test = (rqstp->rq_proc == NLMPROC_TEST ||
- rqstp->rq_proc == NLMPROC_TEST_MSG);
- int mode;
- __be32 error = 0;
+ bool is_test = (rqstp->rq_proc == NLMPROC_TEST ||
+ rqstp->rq_proc == NLMPROC_TEST_MSG);
+ struct file_lock *fl = &lock->fl;
+ struct nlm_file *file = NULL;
+ __be32 error;
+ int mode;
- /* nfsd callbacks must have been installed for this procedure */
- if (!nlmsvc_ops)
+ if (xdr_lock->fh.len != NLM3_FHSIZE)
return nlm_lck_denied_nolocks;
+ lock->fh.size = xdr_lock->fh.len;
+ memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len);
- /* Obtain host handle */
- if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len))
- || (argp->monitor && nsm_monitor(host) < 0))
- goto no_locks;
- *hostp = host;
+ lock->oh.len = xdr_lock->oh.len;
+ lock->oh.data = xdr_lock->oh.data;
- /* Obtain file pointer. Not used by FREE_ALL call. */
- if (filp != NULL) {
- mode = lock_to_openmode(&lock->fl);
+ lock->svid = xdr_lock->uppid;
+ lock->lock_start = xdr_lock->l_offset;
+ lock->lock_len = xdr_lock->l_len;
- if (is_test)
- mode = O_RDWR;
+ locks_init_lock(fl);
+ fl->c.flc_type = type;
+ lockd_set_file_lock_range3(fl, lock->lock_start, lock->lock_len);
- error = cast_status(nlm_lookup_file(rqstp, &file, lock, mode));
- if (error != 0)
- goto no_locks;
- *filp = file;
+ mode = lock_to_openmode(fl);
+ if (is_test)
+ mode = O_RDWR;
- /* Set up the missing parts of the file_lock structure */
- lock->fl.c.flc_flags = FL_POSIX;
- if (is_test)
- lock->fl.c.flc_file = nlmsvc_file_file(file);
- else
- lock->fl.c.flc_file = file->f_file[mode];
- lock->fl.c.flc_pid = current->tgid;
- lock->fl.fl_lmops = &nlmsvc_lock_operations;
- nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
- if (!lock->fl.c.flc_owner) {
- /* lockowner allocation has failed */
- nlmsvc_release_host(host);
- return nlm_lck_denied_nolocks;
- }
+ error = nlm_lookup_file(rqstp, &file, lock, mode);
+ switch (error) {
+ case nlm_granted:
+ break;
+ case nlm__int__stale_fh:
+ case nlm__int__failed:
+ return nlm_lck_denied_nolocks;
+ default:
+ return error;
}
+ *filp = file;
- return 0;
+ fl->c.flc_flags = FL_POSIX;
+ if (is_test)
+ fl->c.flc_file = nlmsvc_file_file(file);
+ else
+ fl->c.flc_file = file->f_file[mode];
+ fl->c.flc_pid = current->tgid;
+ fl->fl_lmops = &nlmsvc_lock_operations;
+ nlmsvc_locks_init_private(fl, host, (pid_t)lock->svid);
+ if (!fl->c.flc_owner)
+ return nlm_lck_denied_nolocks;
-no_locks:
- nlmsvc_release_host(host);
- if (error)
- return error;
- return nlm_lck_denied_nolocks;
+ return nlm_granted;
}
-/*
- * NULL: Test for presence of service
+static inline __be32 cast_status(__be32 status)
+{
+ switch (status) {
+ case nlm_granted:
+ case nlm_lck_denied:
+ case nlm_lck_denied_nolocks:
+ case nlm_lck_blocked:
+ case nlm_lck_denied_grace_period:
+ case nlm__int__drop_reply:
+ break;
+ case nlm__int__deadlock:
+ status = nlm_lck_denied;
+ break;
+ default:
+ pr_warn_once("lockd: unhandled internal status %u\n",
+ be32_to_cpu(status));
+ status = nlm_lck_denied_nolocks;
+ break;
+ }
+ return status;
+}
+
+/**
+ * nlmsvc_proc_null - NULL: Test for presence of service
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * void NLM_NULL(void) = 0;
*/
-static __be32
-nlmsvc_proc_null(struct svc_rqst *rqstp)
+static __be32 nlmsvc_proc_null(struct svc_rqst *rqstp)
{
- dprintk("lockd: NULL called\n");
return rpc_success;
}
-/*
- * TEST: Check for conflicting lock
+/**
+ * nlmsvc_proc_test - TEST: Check for conflicting lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ * nlm_testres NLM_TEST(nlm_testargs) = 1;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The server would be able to grant the
+ * requested lock.
+ * %LCK_DENIED: The requested lock conflicted with existing
+ * lock reservations for the file.
+ * %LCK_DENIED_NOLOCKS: The server could not allocate the resources
+ * needed to process the request.
+ * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is
+ * re-establishing existing locks, and is not
+ * yet ready to accept normal service requests.
*/
-static __be32
-__nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
+static __be32 nlmsvc_proc_test(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
+ struct nlm_testargs_wrapper *argp = rqstp->rq_argp;
+ unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
+ struct nlm_testres_wrapper *resp = rqstp->rq_resp;
+ struct nlm_file *file = NULL;
struct nlm_host *host;
- struct nlm_file *file;
- __be32 rc = rpc_success;
- dprintk("lockd: TEST called\n");
- resp->cookie = argp->cookie;
+ resp->xdrgen.cookie = argp->xdrgen.cookie;
- /* Obtain client and file */
- if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
- return resp->status == nlm__int__drop_reply ?
- rpc_drop_reply : rpc_success;
+ resp->xdrgen.test_stat.stat = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ goto out;
- /* Now check for conflicting locks */
- resp->status = cast_status(nlmsvc_testlock(rqstp, file, host,
- &argp->lock, &resp->lock));
- if (resp->status == nlm__int__drop_reply)
- rc = rpc_drop_reply;
- else
- dprintk("lockd: TEST status %d vers %d\n",
- ntohl(resp->status), rqstp->rq_vers);
+ resp->xdrgen.test_stat.stat =
+ nlm3svc_lookup_file(rqstp, host, &argp->lock, &file,
+ &argp->xdrgen.alock, type);
+ if (resp->xdrgen.test_stat.stat)
+ goto out;
+ resp->xdrgen.test_stat.stat =
+ cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock,
+ &resp->lock));
nlmsvc_release_lockowner(&argp->lock);
+
+ if (resp->xdrgen.test_stat.stat == nlm_lck_denied) {
+ struct lockd_lock *conf = &resp->lock;
+ struct nlm_holder *holder = &resp->xdrgen.test_stat.u.holder;
+
+ holder->exclusive = (conf->fl.c.flc_type != F_RDLCK);
+ holder->uppid = conf->svid;
+ holder->oh.len = conf->oh.len;
+ holder->oh.data = conf->oh.data;
+ holder->l_offset = min_t(loff_t, conf->fl.fl_start,
+ NLM_OFFSET_MAX);
+ if (conf->fl.fl_end == OFFSET_MAX)
+ holder->l_len = 0;
+ else
+ holder->l_len = min_t(loff_t,
+ conf->fl.fl_end -
+ conf->fl.fl_start + 1,
+ NLM_OFFSET_MAX);
+ }
+
+out:
+ if (file)
+ nlm_release_file(file);
nlmsvc_release_host(host);
- nlm_release_file(file);
- return rc;
+ return resp->xdrgen.test_stat.stat == nlm__int__drop_reply ?
+ rpc_drop_reply : rpc_success;
}
static __be32
-nlmsvc_proc_test(struct svc_rqst *rqstp)
+nlmsvc_do_lock(struct svc_rqst *rqstp, bool monitored)
{
- return __nlmsvc_proc_test(rqstp, rqstp->rq_resp);
-}
+ struct nlm_lockargs_wrapper *argp = rqstp->rq_argp;
+ unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
+ struct nlm_res_wrapper *resp = rqstp->rq_resp;
+ struct nlm_file *file = NULL;
+ struct nlm_host *host = NULL;
-static __be32
-__nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp)
-{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_host *host;
- struct nlm_file *file;
- __be32 rc = rpc_success;
+ resp->xdrgen.cookie = argp->xdrgen.cookie;
- dprintk("lockd: LOCK called\n");
+ resp->xdrgen.stat.stat = nlm_netobj_to_cookie(&argp->cookie,
+ &argp->xdrgen.cookie);
+ if (resp->xdrgen.stat.stat)
+ goto out;
- resp->cookie = argp->cookie;
+ resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name,
+ monitored);
+ if (!host)
+ goto out;
- /* Obtain client and file */
- if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
- return resp->status == nlm__int__drop_reply ?
- rpc_drop_reply : rpc_success;
+ resp->xdrgen.stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock,
+ &file, &argp->xdrgen.alock,
+ type);
+ if (resp->xdrgen.stat.stat)
+ goto out;
- /* Now try to lock the file */
- resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
- argp->block, &argp->cookie,
- argp->reclaim));
- if (resp->status == nlm__int__drop_reply)
- rc = rpc_drop_reply;
- else
- dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
+ resp->xdrgen.stat.stat = cast_status(nlmsvc_lock(rqstp, file, host,
+ &argp->lock,
+ argp->xdrgen.block,
+ &argp->cookie,
+ argp->xdrgen.reclaim));
nlmsvc_release_lockowner(&argp->lock);
+
+out:
+ if (file)
+ nlm_release_file(file);
nlmsvc_release_host(host);
- nlm_release_file(file);
- return rc;
+ return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
+ rpc_drop_reply : rpc_success;
}
+/**
+ * nlmsvc_proc_lock - LOCK: Establish a monitored lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ * nlm_res NLM_LOCK(nlm_lockargs) = 2;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The requested lock was granted.
+ * %LCK_DENIED: The requested lock conflicted with existing
+ * lock reservations for the file.
+ * %LCK_DENIED_NOLOCKS: The server could not allocate the resources
+ * needed to process the request.
+ * %LCK_BLOCKED: The blocking request cannot be granted
+ * immediately. The server will send an
+ * NLM_GRANTED callback to the client when
+ * the lock can be granted.
+ * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is
+ * re-establishing existing locks, and is not
+ * yet ready to accept normal service requests.
+ */
static __be32
nlmsvc_proc_lock(struct svc_rqst *rqstp)
{
- return __nlmsvc_proc_lock(rqstp, rqstp->rq_resp);
+ return nlmsvc_do_lock(rqstp, true);
}
+/**
+ * nlmsvc_proc_cancel - CANCEL: Cancel an outstanding blocked lock request
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ * nlm_res NLM_CANCEL(nlm_cancargs) = 3;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The requested lock was canceled.
+ * %LCK_DENIED: There was no lock to cancel.
+ * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is
+ * re-establishing existing locks, and is not
+ * yet ready to accept normal service requests.
+ *
+ * The Linux NLM server implementation also returns:
+ * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated.
+ */
static __be32
-__nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_res *resp)
+nlmsvc_proc_cancel(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_host *host;
- struct nlm_file *file;
+ struct nlm_cancargs_wrapper *argp = rqstp->rq_argp;
+ unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
+ struct nlm_res_wrapper *resp = rqstp->rq_resp;
struct net *net = SVC_NET(rqstp);
+ struct nlm_host *host = NULL;
+ struct nlm_file *file = NULL;
- dprintk("lockd: CANCEL called\n");
-
- resp->cookie = argp->cookie;
+ resp->xdrgen.cookie = argp->xdrgen.cookie;
- /* Don't accept requests during grace period */
- if (locks_in_grace(net)) {
- resp->status = nlm_lck_denied_grace_period;
- return rpc_success;
- }
+ resp->xdrgen.stat.stat = nlm_lck_denied_grace_period;
+ if (locks_in_grace(net))
+ goto out;
- /* Obtain client and file */
- if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
- return resp->status == nlm__int__drop_reply ?
- rpc_drop_reply : rpc_success;
+ resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ goto out;
- /* Try to cancel request. */
- resp->status = cast_status(nlmsvc_cancel_blocked(net, file, &argp->lock));
+ resp->xdrgen.stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock,
+ &file, &argp->xdrgen.alock,
+ type);
+ if (resp->xdrgen.stat.stat)
+ goto out;
- dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
+ resp->xdrgen.stat.stat = nlmsvc_cancel_blocked(net, file, &argp->lock);
nlmsvc_release_lockowner(&argp->lock);
- nlmsvc_release_host(host);
- nlm_release_file(file);
- return rpc_success;
-}
-static __be32
-nlmsvc_proc_cancel(struct svc_rqst *rqstp)
-{
- return __nlmsvc_proc_cancel(rqstp, rqstp->rq_resp);
+out:
+ if (file)
+ nlm_release_file(file);
+ nlmsvc_release_host(host);
+ return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
+ rpc_drop_reply : rpc_success;
}
-/*
- * UNLOCK: release a lock
+/**
+ * nlmsvc_proc_unlock - UNLOCK: Remove a lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ * nlm_res NLM_UNLOCK(nlm_unlockargs) = 4;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The requested lock was released.
+ * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is
+ * re-establishing existing locks, and is not
+ * yet ready to accept normal service requests.
+ *
+ * The Linux NLM server implementation also returns:
+ * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated.
*/
static __be32
-__nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp)
+nlmsvc_proc_unlock(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_host *host;
- struct nlm_file *file;
+ struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_res_wrapper *resp = rqstp->rq_resp;
struct net *net = SVC_NET(rqstp);
+ struct nlm_host *host = NULL;
+ struct nlm_file *file = NULL;
- dprintk("lockd: UNLOCK called\n");
+ resp->xdrgen.cookie = argp->xdrgen.cookie;
- resp->cookie = argp->cookie;
+ resp->xdrgen.stat.stat = nlm_lck_denied_grace_period;
+ if (locks_in_grace(net))
+ goto out;
- /* Don't accept new lock requests during grace period */
- if (locks_in_grace(net)) {
- resp->status = nlm_lck_denied_grace_period;
- return rpc_success;
- }
-
- /* Obtain client and file */
- if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
- return resp->status == nlm__int__drop_reply ?
- rpc_drop_reply : rpc_success;
+ resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ goto out;
- /* Now try to remove the lock */
- resp->status = cast_status(nlmsvc_unlock(net, file, &argp->lock));
+ resp->xdrgen.stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock,
+ &file, &argp->xdrgen.alock,
+ F_UNLCK);
+ if (resp->xdrgen.stat.stat)
+ goto out;
- dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
+ resp->xdrgen.stat.stat = nlmsvc_unlock(net, file, &argp->lock);
nlmsvc_release_lockowner(&argp->lock);
- nlmsvc_release_host(host);
- nlm_release_file(file);
- return rpc_success;
-}
-static __be32
-nlmsvc_proc_unlock(struct svc_rqst *rqstp)
-{
- return __nlmsvc_proc_unlock(rqstp, rqstp->rq_resp);
+out:
+ if (file)
+ nlm_release_file(file);
+ nlmsvc_release_host(host);
+ return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
+ rpc_drop_reply : rpc_success;
}
-/*
- * GRANTED: A server calls us to tell that a process' lock request
- * was granted
+/**
+ * nlmsvc_proc_granted - GRANTED: Blocked lock has been granted
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ *
+ * RPC synopsis:
+ * nlm_res NLM_GRANTED(nlm_testargs) = 5;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The granted lock was accepted.
+ * %LCK_DENIED: The procedure failed, possibly due to
+ * internal resource constraints.
+ * %LCK_DENIED_GRACE_PERIOD: The client host recently restarted and
+ * its NLM is re-establishing existing locks,
+ * so it is not yet ready to accept callbacks.
*/
static __be32
-__nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_res *resp)
+nlmsvc_proc_granted(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
+ struct nlm_testargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_res_wrapper *resp = rqstp->rq_resp;
- resp->cookie = argp->cookie;
+ resp->xdrgen.cookie = argp->xdrgen.cookie;
- dprintk("lockd: GRANTED called\n");
- resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
- dprintk("lockd: GRANTED status %d\n", ntohl(resp->status));
- return rpc_success;
-}
+ resp->xdrgen.stat.stat = nlm_lock_to_lockd_lock(&argp->lock,
+ &argp->xdrgen.alock);
+ if (resp->xdrgen.stat.stat)
+ goto out;
-static __be32
-nlmsvc_proc_granted(struct svc_rqst *rqstp)
-{
- return __nlmsvc_proc_granted(rqstp, rqstp->rq_resp);
+ resp->xdrgen.stat.stat = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
+
+out:
+ return rpc_success;
}
/*
@@ -342,20 +573,13 @@ static const struct rpc_call_ops nlmsvc_callback_ops = {
* because we send the callback before the reply proper. I hope this
* doesn't break any clients.
*/
-static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc,
- __be32 (*func)(struct svc_rqst *, struct nlm_res *))
+static __be32
+nlmsvc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc,
+ __be32 (*func)(struct svc_rqst *, struct lockd_res *))
{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_host *host;
struct nlm_rqst *call;
__be32 stat;
- host = nlmsvc_lookup_host(rqstp,
- argp->lock.caller,
- argp->lock.len);
- if (host == NULL)
- return rpc_system_err;
-
call = nlm_alloc_call(host);
nlmsvc_release_host(host);
if (call == NULL)
@@ -373,440 +597,819 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc,
return rpc_success;
}
-static __be32 nlmsvc_proc_test_msg(struct svc_rqst *rqstp)
+static __be32
+__nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
- dprintk("lockd: TEST_MSG called\n");
- return nlmsvc_callback(rqstp, NLMPROC_TEST_RES, __nlmsvc_proc_test);
+ struct nlm_testargs_wrapper *argp = rqstp->rq_argp;
+ unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
+ struct nlm_lockowner *owner;
+ struct nlm_file *file = NULL;
+ struct nlm_host *host = NULL;
+
+ resp->status = nlm_lck_denied_nolocks;
+ if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
+ goto out;
+
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ goto out;
+
+ resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock,
+ &file, &argp->xdrgen.alock, type);
+ if (resp->status)
+ goto out;
+
+ owner = argp->lock.fl.c.flc_owner;
+ resp->status = cast_status(nlmsvc_testlock(rqstp, file, host,
+ &argp->lock, &resp->lock));
+ nlmsvc_put_lockowner(owner);
+
+out:
+ if (file)
+ nlm_release_file(file);
+ nlmsvc_release_host(host);
+ return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success;
}
-static __be32 nlmsvc_proc_lock_msg(struct svc_rqst *rqstp)
+/**
+ * nlmsvc_proc_test_msg - TEST_MSG: Check for conflicting lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ * %rpc_garbage_args: The request arguments are malformed.
+ * %rpc_system_err: RPC execution failed.
+ *
+ * RPC synopsis:
+ * void NLMPROC_TEST_MSG(nlm_testargs) = 6;
+ *
+ * The response to this request is delivered via the TEST_RES procedure.
+ */
+static __be32 nlmsvc_proc_test_msg(struct svc_rqst *rqstp)
{
- dprintk("lockd: LOCK_MSG called\n");
- return nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, __nlmsvc_proc_lock);
+ struct nlm_testargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_host *host;
+
+ if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN)
+ return rpc_garbage_args;
+
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ return rpc_system_err;
+
+ return nlmsvc_callback(rqstp, host, NLMPROC_TEST_RES,
+ __nlmsvc_proc_test_msg);
}
-static __be32 nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp)
+static __be32
+__nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
- dprintk("lockd: CANCEL_MSG called\n");
- return nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, __nlmsvc_proc_cancel);
+ struct nlm_lockargs_wrapper *argp = rqstp->rq_argp;
+ unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
+ struct nlm_file *file = NULL;
+ struct nlm_host *host = NULL;
+
+ resp->status = nlm_lck_denied_nolocks;
+ if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
+ goto out;
+
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true);
+ if (!host)
+ goto out;
+
+ resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock,
+ &file, &argp->xdrgen.alock, type);
+ if (resp->status)
+ goto out;
+
+ resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
+ argp->xdrgen.block, &resp->cookie,
+ argp->xdrgen.reclaim));
+ nlmsvc_release_lockowner(&argp->lock);
+
+out:
+ if (file)
+ nlm_release_file(file);
+ nlmsvc_release_host(host);
+ return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success;
}
-static __be32
-nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp)
+/**
+ * nlmsvc_proc_lock_msg - LOCK_MSG: Establish a monitored lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ * %rpc_garbage_args: The request arguments are malformed.
+ * %rpc_system_err: RPC execution failed.
+ *
+ * RPC synopsis:
+ * void NLMPROC_LOCK_MSG(nlm_lockargs) = 7;
+ *
+ * The response to this request is delivered via the LOCK_RES procedure.
+ */
+static __be32 nlmsvc_proc_lock_msg(struct svc_rqst *rqstp)
{
- dprintk("lockd: UNLOCK_MSG called\n");
- return nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, __nlmsvc_proc_unlock);
+ struct nlm_lockargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_host *host;
+
+ if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN)
+ return rpc_garbage_args;
+
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ return rpc_system_err;
+
+ return nlmsvc_callback(rqstp, host, NLMPROC_LOCK_RES,
+ __nlmsvc_proc_lock_msg);
}
static __be32
-nlmsvc_proc_granted_msg(struct svc_rqst *rqstp)
+__nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
{
- dprintk("lockd: GRANTED_MSG called\n");
- return nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, __nlmsvc_proc_granted);
+ struct nlm_cancargs_wrapper *argp = rqstp->rq_argp;
+ unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
+ struct net *net = SVC_NET(rqstp);
+ struct nlm_file *file = NULL;
+ struct nlm_host *host = NULL;
+
+ resp->status = nlm_lck_denied_nolocks;
+ if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
+ goto out;
+
+ resp->status = nlm_lck_denied_grace_period;
+ if (locks_in_grace(net))
+ goto out;
+
+ resp->status = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ goto out;
+
+ resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock,
+ &file, &argp->xdrgen.alock, type);
+ if (resp->status)
+ goto out;
+
+ resp->status = nlmsvc_cancel_blocked(net, file, &argp->lock);
+ nlmsvc_release_lockowner(&argp->lock);
+
+out:
+ if (file)
+ nlm_release_file(file);
+ nlmsvc_release_host(host);
+ return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success;
}
-/*
- * SHARE: create a DOS share or alter existing share.
+/**
+ * nlmsvc_proc_cancel_msg - CANCEL_MSG: Cancel an outstanding lock request
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ * %rpc_garbage_args: The request arguments are malformed.
+ * %rpc_system_err: RPC execution failed.
+ *
+ * RPC synopsis:
+ * void NLMPROC_CANCEL_MSG(nlm_cancargs) = 8;
+ *
+ * The response to this request is delivered via the CANCEL_RES procedure.
*/
-static __be32
-nlmsvc_proc_share(struct svc_rqst *rqstp)
+static __be32 nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_res *resp = rqstp->rq_resp;
- struct nlm_host *host;
- struct nlm_file *file;
+ struct nlm_cancargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_host *host;
+
+ if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN)
+ return rpc_garbage_args;
- dprintk("lockd: SHARE called\n");
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ return rpc_system_err;
- resp->cookie = argp->cookie;
+ return nlmsvc_callback(rqstp, host, NLMPROC_CANCEL_RES,
+ __nlmsvc_proc_cancel_msg);
+}
- /* Don't accept new lock requests during grace period */
- if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) {
- resp->status = nlm_lck_denied_grace_period;
- return rpc_success;
- }
+static __be32
+__nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
+{
+ struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp;
+ struct net *net = SVC_NET(rqstp);
+ struct nlm_file *file = NULL;
+ struct nlm_host *host = NULL;
+
+ resp->status = nlm_lck_denied_nolocks;
+ if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
+ goto out;
+
+ resp->status = nlm_lck_denied_grace_period;
+ if (locks_in_grace(net))
+ goto out;
- /* Obtain client and file */
- if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
- return resp->status == nlm__int__drop_reply ?
- rpc_drop_reply : rpc_success;
+ resp->status = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ goto out;
- /* Now try to create the share */
- resp->status = cast_status(nlmsvc_share_file(host, file, &argp->lock.oh,
- argp->fsm_access,
- argp->fsm_mode));
+ resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock,
+ &file, &argp->xdrgen.alock, F_UNLCK);
+ if (resp->status)
+ goto out;
- dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
+ resp->status = nlmsvc_unlock(net, file, &argp->lock);
nlmsvc_release_lockowner(&argp->lock);
+
+out:
+ if (file)
+ nlm_release_file(file);
nlmsvc_release_host(host);
- nlm_release_file(file);
- return rpc_success;
+ return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success;
}
-/*
- * UNSHARE: Release a DOS share.
+/**
+ * nlmsvc_proc_unlock_msg - UNLOCK_MSG: Remove an existing lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ * %rpc_garbage_args: The request arguments are malformed.
+ * %rpc_system_err: RPC execution failed.
+ *
+ * RPC synopsis:
+ * void NLMPROC_UNLOCK_MSG(nlm_unlockargs) = 9;
+ *
+ * The response to this request is delivered via the UNLOCK_RES procedure.
*/
-static __be32
-nlmsvc_proc_unshare(struct svc_rqst *rqstp)
+static __be32 nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_res *resp = rqstp->rq_resp;
- struct nlm_host *host;
- struct nlm_file *file;
+ struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_host *host;
- dprintk("lockd: UNSHARE called\n");
+ if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN)
+ return rpc_garbage_args;
- resp->cookie = argp->cookie;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ return rpc_system_err;
- /* Don't accept requests during grace period */
- if (locks_in_grace(SVC_NET(rqstp))) {
- resp->status = nlm_lck_denied_grace_period;
- return rpc_success;
- }
+ return nlmsvc_callback(rqstp, host, NLMPROC_UNLOCK_RES,
+ __nlmsvc_proc_unlock_msg);
+}
- /* Obtain client and file */
- if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
- return resp->status == nlm__int__drop_reply ?
- rpc_drop_reply : rpc_success;
+static __be32
+__nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct lockd_res *resp)
+{
+ struct nlm_testargs_wrapper *argp = rqstp->rq_argp;
- /* Now try to unshare the file */
- resp->status = cast_status(nlmsvc_unshare_file(host, file,
- &argp->lock.oh));
+ resp->status = nlm_lck_denied;
+ if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
+ goto out;
- dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
- nlmsvc_release_lockowner(&argp->lock);
- nlmsvc_release_host(host);
- nlm_release_file(file);
+ if (nlm_lock_to_lockd_lock(&argp->lock, &argp->xdrgen.alock))
+ goto out;
+
+ resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
+
+out:
return rpc_success;
}
-/*
- * NM_LOCK: Create an unmonitored lock
+/**
+ * nlmsvc_proc_granted_msg - GRANTED_MSG: Blocked lock has been granted
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_garbage_args: The request arguments are malformed.
+ * %rpc_system_err: RPC execution failed.
+ *
+ * RPC synopsis:
+ * void NLMPROC_GRANTED_MSG(nlm_testargs) = 10;
+ *
+ * The response to this request is delivered via the GRANTED_RES procedure.
*/
-static __be32
-nlmsvc_proc_nm_lock(struct svc_rqst *rqstp)
+static __be32 nlmsvc_proc_granted_msg(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
+ struct nlm_testargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_host *host;
- dprintk("lockd: NM_LOCK called\n");
+ if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN)
+ return rpc_garbage_args;
- argp->monitor = 0; /* just clean the monitor flag */
- return nlmsvc_proc_lock(rqstp);
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ if (!host)
+ return rpc_system_err;
+
+ return nlmsvc_callback(rqstp, host, NLMPROC_GRANTED_RES,
+ __nlmsvc_proc_granted_msg);
}
-/*
- * FREE_ALL: Release all locks and shares held by client
+/**
+ * nlmsvc_proc_granted_res - GRANTED_RES: Lock Granted result
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ *
+ * RPC synopsis:
+ * void NLMPROC_GRANTED_RES(nlm_res) = 15;
*/
-static __be32
-nlmsvc_proc_free_all(struct svc_rqst *rqstp)
+static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_host *host;
+ struct nlm_res_wrapper *argp = rqstp->rq_argp;
- /* Obtain client */
- if (nlmsvc_retrieve_args(rqstp, argp, &host, NULL))
+ if (!nlmsvc_ops)
return rpc_success;
- nlmsvc_free_host_resources(host);
- nlmsvc_release_host(host);
+ if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
+ return rpc_success;
+ nlmsvc_grant_reply(&argp->cookie, argp->xdrgen.stat.stat);
+
return rpc_success;
}
-/*
- * SM_NOTIFY: private callback from statd (not part of official NLM proto)
+/**
+ * nlmsvc_proc_sm_notify - SM_NOTIFY: Peer has rebooted
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_system_err: RPC execution failed.
+ *
+ * The SM_NOTIFY procedure is a private callback from Linux statd and is
+ * not part of the official NLM protocol.
+ *
+ * RPC synopsis:
+ * void NLM_SM_NOTIFY(nlm_notifyargs) = 16;
*/
-static __be32
-nlmsvc_proc_sm_notify(struct svc_rqst *rqstp)
+static __be32 nlmsvc_proc_sm_notify(struct svc_rqst *rqstp)
{
- struct nlm_reboot *argp = rqstp->rq_argp;
-
- dprintk("lockd: SM_NOTIFY called\n");
+ struct nlm_notifyargs_wrapper *argp = rqstp->rq_argp;
+ struct lockd_reboot *reboot = &argp->reboot;
if (!nlm_privileged_requester(rqstp)) {
char buf[RPC_MAX_ADDRBUFLEN];
- printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
- svc_print_addr(rqstp, buf, sizeof(buf)));
+
+ pr_warn("lockd: rejected NSM callback from %s\n",
+ svc_print_addr(rqstp, buf, sizeof(buf)));
return rpc_system_err;
}
- nlm_host_rebooted(SVC_NET(rqstp), argp);
+ reboot->len = argp->xdrgen.notify.name.len;
+ reboot->mon = (char *)argp->xdrgen.notify.name.data;
+ reboot->state = argp->xdrgen.notify.state;
+ memcpy(&reboot->priv.data, argp->xdrgen.private,
+ sizeof(reboot->priv.data));
+
+ nlm_host_rebooted(SVC_NET(rqstp), reboot);
+
return rpc_success;
}
-/*
- * client sent a GRANTED_RES, let's remove the associated block
+/**
+ * nlmsvc_proc_unused - stub for unused procedures
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_proc_unavail: Program can't support procedure.
*/
-static __be32
-nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
+static __be32 nlmsvc_proc_unused(struct svc_rqst *rqstp)
{
- struct nlm_res *argp = rqstp->rq_argp;
+ return rpc_proc_unavail;
+}
- if (!nlmsvc_ops)
- return rpc_success;
+/**
+ * nlmsvc_proc_share - SHARE: Open a file using DOS file-sharing modes
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ * nlm_shareres NLM_SHARE(nlm_shareargs) = 20;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The requested share lock was granted.
+ * %LCK_DENIED: The requested lock conflicted with existing
+ * lock reservations for the file.
+ * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is
+ * re-establishing existing locks, and is not
+ * yet ready to accept normal service requests.
+ *
+ * The Linux NLM server implementation also returns:
+ * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated.
+ */
+static __be32 nlmsvc_proc_share(struct svc_rqst *rqstp)
+{
+ struct nlm_shareargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_shareres_wrapper *resp = rqstp->rq_resp;
+ struct lockd_lock *lock = &argp->lock;
+ struct nlm_host *host = NULL;
+ struct nlm_file *file = NULL;
+ struct nlm_lock xdr_lock = {
+ .fh = argp->xdrgen.share.fh,
+ .oh = argp->xdrgen.share.oh,
+ .uppid = LOCKD_SHARE_SVID,
+ };
- dprintk("lockd: GRANTED_RES called\n");
+ resp->xdrgen.cookie = argp->xdrgen.cookie;
- nlmsvc_grant_reply(&argp->cookie, argp->status);
- return rpc_success;
+ resp->xdrgen.stat = nlm_lck_denied_grace_period;
+ if (locks_in_grace(SVC_NET(rqstp)) && !argp->xdrgen.reclaim)
+ goto out;
+
+ resp->xdrgen.stat = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, false);
+ if (!host)
+ goto out;
+
+ resp->xdrgen.stat = nlm3svc_lookup_file(rqstp, host, lock, &file,
+ &xdr_lock, F_RDLCK);
+ if (resp->xdrgen.stat)
+ goto out;
+
+ resp->xdrgen.stat = nlmsvc_share_file(host, file, &lock->oh,
+ argp->xdrgen.share.access,
+ argp->xdrgen.share.mode);
+
+ nlmsvc_release_lockowner(lock);
+
+out:
+ if (file)
+ nlm_release_file(file);
+ nlmsvc_release_host(host);
+ return resp->xdrgen.stat == nlm__int__drop_reply ?
+ rpc_drop_reply : rpc_success;
}
-static __be32
-nlmsvc_proc_unused(struct svc_rqst *rqstp)
+/**
+ * nlmsvc_proc_unshare - UNSHARE: Release a share reservation
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ * nlm_shareres NLM_UNSHARE(nlm_shareargs) = 21;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The share reservation was released.
+ * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is
+ * re-establishing existing locks, and is not
+ * yet ready to accept normal service requests.
+ *
+ * The Linux NLM server implementation also returns:
+ * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated.
+ */
+static __be32 nlmsvc_proc_unshare(struct svc_rqst *rqstp)
{
- return rpc_proc_unavail;
+ struct nlm_shareargs_wrapper *argp = rqstp->rq_argp;
+ struct nlm_shareres_wrapper *resp = rqstp->rq_resp;
+ struct lockd_lock *lock = &argp->lock;
+ struct nlm_lock xdr_lock = {
+ .fh = argp->xdrgen.share.fh,
+ .oh = argp->xdrgen.share.oh,
+ .uppid = LOCKD_SHARE_SVID,
+ };
+ struct nlm_host *host = NULL;
+ struct nlm_file *file = NULL;
+
+ resp->xdrgen.cookie = argp->xdrgen.cookie;
+
+ resp->xdrgen.stat = nlm_lck_denied_grace_period;
+ if (locks_in_grace(SVC_NET(rqstp)))
+ goto out;
+
+ resp->xdrgen.stat = nlm_lck_denied_nolocks;
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, false);
+ if (!host)
+ goto out;
+
+ resp->xdrgen.stat = nlm3svc_lookup_file(rqstp, host, lock, &file,
+ &xdr_lock, F_RDLCK);
+ if (resp->xdrgen.stat)
+ goto out;
+
+ resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh);
+
+ nlmsvc_release_lockowner(lock);
+
+out:
+ if (file)
+ nlm_release_file(file);
+ nlmsvc_release_host(host);
+ return resp->xdrgen.stat == nlm__int__drop_reply ?
+ rpc_drop_reply : rpc_success;
}
-/*
- * NLM Server procedures.
+/**
+ * nlmsvc_proc_nm_lock - NM_LOCK: Establish a non-monitored lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ * %rpc_drop_reply: Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ * nlm_res NLM_NM_LOCK(nlm_lockargs) = 22;
+ *
+ * Permissible procedure status codes:
+ * %LCK_GRANTED: The requested lock was granted.
+ * %LCK_DENIED: The requested lock conflicted with existing
+ * lock reservations for the file.
+ * %LCK_DENIED_NOLOCKS: The server could not allocate the resources
+ * needed to process the request.
+ * %LCK_BLOCKED: The blocking request cannot be granted
+ * immediately. The server will send an
+ * NLM_GRANTED callback to the client when
+ * the lock can be granted.
+ * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is
+ * re-establishing existing locks, and is not
+ * yet ready to accept normal service requests.
+ */
+static __be32 nlmsvc_proc_nm_lock(struct svc_rqst *rqstp)
+{
+ return nlmsvc_do_lock(rqstp, false);
+}
+
+/**
+ * nlmsvc_proc_free_all - FREE_ALL: Discard client's lock and share state
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ * %rpc_success: RPC executed successfully.
+ *
+ * RPC synopsis:
+ * void NLMPROC_FREE_ALL(nlm_notify) = 23;
*/
+static __be32 nlmsvc_proc_free_all(struct svc_rqst *rqstp)
+{
+ struct nlm_notify_wrapper *argp = rqstp->rq_argp;
+ struct nlm_host *host;
+
+ host = nlm3svc_lookup_host(rqstp, argp->xdrgen.name, false);
+ if (!host)
+ goto out;
-struct nlm_void { int dummy; };
+ nlmsvc_free_host_resources(host);
+
+ nlmsvc_release_host(host);
-#define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */
-#define St 1 /* status */
-#define No (1+1024/4) /* Net Obj */
-#define Rg 2 /* range - offset + size */
+out:
+ return rpc_success;
+}
+
+/*
+ * NLMv3 Server procedures.
+ */
static const struct svc_procedure nlmsvc_procedures[24] = {
- [NLMPROC_NULL] = {
- .pc_func = nlmsvc_proc_null,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_void),
- .pc_argzero = sizeof(struct nlm_void),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "NULL",
+ [NLM_NULL] = {
+ .pc_func = nlmsvc_proc_null,
+ .pc_decode = nlm_svc_decode_void,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = XDR_void,
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "NULL",
},
- [NLMPROC_TEST] = {
- .pc_func = nlmsvc_proc_test,
- .pc_decode = nlmsvc_decode_testargs,
- .pc_encode = nlmsvc_encode_testres,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St+2+No+Rg,
- .pc_name = "TEST",
+ [NLM_TEST] = {
+ .pc_func = nlmsvc_proc_test,
+ .pc_decode = nlm_svc_decode_nlm_testargs,
+ .pc_encode = nlm_svc_encode_nlm_testres,
+ .pc_argsize = sizeof(struct nlm_testargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_testres_wrapper),
+ .pc_xdrressize = NLM3_nlm_testres_sz,
+ .pc_name = "TEST",
},
- [NLMPROC_LOCK] = {
- .pc_func = nlmsvc_proc_lock,
- .pc_decode = nlmsvc_decode_lockargs,
- .pc_encode = nlmsvc_encode_res,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St,
- .pc_name = "LOCK",
+ [NLM_LOCK] = {
+ .pc_func = nlmsvc_proc_lock,
+ .pc_decode = nlm_svc_decode_nlm_lockargs,
+ .pc_encode = nlm_svc_encode_nlm_res,
+ .pc_argsize = sizeof(struct nlm_lockargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_res_wrapper),
+ .pc_xdrressize = NLM3_nlm_res_sz,
+ .pc_name = "LOCK",
},
- [NLMPROC_CANCEL] = {
- .pc_func = nlmsvc_proc_cancel,
- .pc_decode = nlmsvc_decode_cancargs,
- .pc_encode = nlmsvc_encode_res,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St,
- .pc_name = "CANCEL",
+ [NLM_CANCEL] = {
+ .pc_func = nlmsvc_proc_cancel,
+ .pc_decode = nlm_svc_decode_nlm_cancargs,
+ .pc_encode = nlm_svc_encode_nlm_res,
+ .pc_argsize = sizeof(struct nlm_cancargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_res_wrapper),
+ .pc_xdrressize = NLM3_nlm_res_sz,
+ .pc_name = "CANCEL",
},
- [NLMPROC_UNLOCK] = {
- .pc_func = nlmsvc_proc_unlock,
- .pc_decode = nlmsvc_decode_unlockargs,
- .pc_encode = nlmsvc_encode_res,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St,
- .pc_name = "UNLOCK",
+ [NLM_UNLOCK] = {
+ .pc_func = nlmsvc_proc_unlock,
+ .pc_decode = nlm_svc_decode_nlm_unlockargs,
+ .pc_encode = nlm_svc_encode_nlm_res,
+ .pc_argsize = sizeof(struct nlm_unlockargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_res_wrapper),
+ .pc_xdrressize = NLM3_nlm_res_sz,
+ .pc_name = "UNLOCK",
},
- [NLMPROC_GRANTED] = {
- .pc_func = nlmsvc_proc_granted,
- .pc_decode = nlmsvc_decode_testargs,
- .pc_encode = nlmsvc_encode_res,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St,
- .pc_name = "GRANTED",
+ [NLM_GRANTED] = {
+ .pc_func = nlmsvc_proc_granted,
+ .pc_decode = nlm_svc_decode_nlm_testargs,
+ .pc_encode = nlm_svc_encode_nlm_res,
+ .pc_argsize = sizeof(struct nlm_testargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_res_wrapper),
+ .pc_xdrressize = NLM3_nlm_res_sz,
+ .pc_name = "GRANTED",
},
- [NLMPROC_TEST_MSG] = {
- .pc_func = nlmsvc_proc_test_msg,
- .pc_decode = nlmsvc_decode_testargs,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "TEST_MSG",
+ [NLM_TEST_MSG] = {
+ .pc_func = nlmsvc_proc_test_msg,
+ .pc_decode = nlm_svc_decode_nlm_testargs,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_testargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "TEST_MSG",
},
- [NLMPROC_LOCK_MSG] = {
- .pc_func = nlmsvc_proc_lock_msg,
- .pc_decode = nlmsvc_decode_lockargs,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "LOCK_MSG",
+ [NLM_LOCK_MSG] = {
+ .pc_func = nlmsvc_proc_lock_msg,
+ .pc_decode = nlm_svc_decode_nlm_lockargs,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_lockargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "LOCK_MSG",
},
- [NLMPROC_CANCEL_MSG] = {
- .pc_func = nlmsvc_proc_cancel_msg,
- .pc_decode = nlmsvc_decode_cancargs,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "CANCEL_MSG",
+ [NLM_CANCEL_MSG] = {
+ .pc_func = nlmsvc_proc_cancel_msg,
+ .pc_decode = nlm_svc_decode_nlm_cancargs,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_cancargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "CANCEL_MSG",
},
- [NLMPROC_UNLOCK_MSG] = {
- .pc_func = nlmsvc_proc_unlock_msg,
- .pc_decode = nlmsvc_decode_unlockargs,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "UNLOCK_MSG",
+ [NLM_UNLOCK_MSG] = {
+ .pc_func = nlmsvc_proc_unlock_msg,
+ .pc_decode = nlm_svc_decode_nlm_unlockargs,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_unlockargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "UNLOCK_MSG",
},
- [NLMPROC_GRANTED_MSG] = {
- .pc_func = nlmsvc_proc_granted_msg,
- .pc_decode = nlmsvc_decode_testargs,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "GRANTED_MSG",
+ [NLM_GRANTED_MSG] = {
+ .pc_func = nlmsvc_proc_granted_msg,
+ .pc_decode = nlm_svc_decode_nlm_testargs,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_testargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "GRANTED_MSG",
},
- [NLMPROC_TEST_RES] = {
- .pc_func = nlmsvc_proc_null,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_res),
- .pc_argzero = sizeof(struct nlm_res),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "TEST_RES",
+ [NLM_TEST_RES] = {
+ .pc_func = nlmsvc_proc_null,
+ .pc_decode = nlm_svc_decode_nlm_testres,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_testres),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "TEST_RES",
},
- [NLMPROC_LOCK_RES] = {
- .pc_func = nlmsvc_proc_null,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_res),
- .pc_argzero = sizeof(struct nlm_res),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "LOCK_RES",
+ [NLM_LOCK_RES] = {
+ .pc_func = nlmsvc_proc_null,
+ .pc_decode = nlm_svc_decode_nlm_res,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_res),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "LOCK_RES",
},
- [NLMPROC_CANCEL_RES] = {
- .pc_func = nlmsvc_proc_null,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_res),
- .pc_argzero = sizeof(struct nlm_res),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "CANCEL_RES",
+ [NLM_CANCEL_RES] = {
+ .pc_func = nlmsvc_proc_null,
+ .pc_decode = nlm_svc_decode_nlm_res,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_res),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "CANCEL_RES",
},
- [NLMPROC_UNLOCK_RES] = {
- .pc_func = nlmsvc_proc_null,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_res),
- .pc_argzero = sizeof(struct nlm_res),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "UNLOCK_RES",
+ [NLM_UNLOCK_RES] = {
+ .pc_func = nlmsvc_proc_null,
+ .pc_decode = nlm_svc_decode_nlm_res,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_res),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "UNLOCK_RES",
},
- [NLMPROC_GRANTED_RES] = {
- .pc_func = nlmsvc_proc_granted_res,
- .pc_decode = nlmsvc_decode_res,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_res),
- .pc_argzero = sizeof(struct nlm_res),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "GRANTED_RES",
+ [NLM_GRANTED_RES] = {
+ .pc_func = nlmsvc_proc_granted_res,
+ .pc_decode = nlm_svc_decode_nlm_res,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_res_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "GRANTED_RES",
},
- [NLMPROC_NSM_NOTIFY] = {
- .pc_func = nlmsvc_proc_sm_notify,
- .pc_decode = nlmsvc_decode_reboot,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_reboot),
- .pc_argzero = sizeof(struct nlm_reboot),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "SM_NOTIFY",
+ [NLM_SM_NOTIFY] = {
+ .pc_func = nlmsvc_proc_sm_notify,
+ .pc_decode = nlm_svc_decode_nlm_notifyargs,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_notifyargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "SM_NOTIFY",
},
[17] = {
- .pc_func = nlmsvc_proc_unused,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_void),
- .pc_argzero = sizeof(struct nlm_void),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "UNUSED",
+ .pc_func = nlmsvc_proc_unused,
+ .pc_decode = nlm_svc_decode_void,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = 0,
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "UNUSED",
},
[18] = {
- .pc_func = nlmsvc_proc_unused,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_void),
- .pc_argzero = sizeof(struct nlm_void),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "UNUSED",
+ .pc_func = nlmsvc_proc_unused,
+ .pc_decode = nlm_svc_decode_void,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = 0,
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "UNUSED",
},
[19] = {
- .pc_func = nlmsvc_proc_unused,
- .pc_decode = nlmsvc_decode_void,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_void),
- .pc_argzero = sizeof(struct nlm_void),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = St,
- .pc_name = "UNUSED",
+ .pc_func = nlmsvc_proc_unused,
+ .pc_decode = nlm_svc_decode_void,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = 0,
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "UNUSED",
},
- [NLMPROC_SHARE] = {
- .pc_func = nlmsvc_proc_share,
- .pc_decode = nlmsvc_decode_shareargs,
- .pc_encode = nlmsvc_encode_shareres,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St+1,
- .pc_name = "SHARE",
+ [NLM_SHARE] = {
+ .pc_func = nlmsvc_proc_share,
+ .pc_decode = nlm_svc_decode_nlm_shareargs,
+ .pc_encode = nlm_svc_encode_nlm_shareres,
+ .pc_argsize = sizeof(struct nlm_shareargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_shareres_wrapper),
+ .pc_xdrressize = NLM3_nlm_shareres_sz,
+ .pc_name = "SHARE",
},
- [NLMPROC_UNSHARE] = {
- .pc_func = nlmsvc_proc_unshare,
- .pc_decode = nlmsvc_decode_shareargs,
- .pc_encode = nlmsvc_encode_shareres,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St+1,
- .pc_name = "UNSHARE",
+ [NLM_UNSHARE] = {
+ .pc_func = nlmsvc_proc_unshare,
+ .pc_decode = nlm_svc_decode_nlm_shareargs,
+ .pc_encode = nlm_svc_encode_nlm_shareres,
+ .pc_argsize = sizeof(struct nlm_shareargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_shareres_wrapper),
+ .pc_xdrressize = NLM3_nlm_shareres_sz,
+ .pc_name = "UNSHARE",
},
- [NLMPROC_NM_LOCK] = {
- .pc_func = nlmsvc_proc_nm_lock,
- .pc_decode = nlmsvc_decode_lockargs,
- .pc_encode = nlmsvc_encode_res,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_res),
- .pc_xdrressize = Ck+St,
- .pc_name = "NM_LOCK",
+ [NLM_NM_LOCK] = {
+ .pc_func = nlmsvc_proc_nm_lock,
+ .pc_decode = nlm_svc_decode_nlm_lockargs,
+ .pc_encode = nlm_svc_encode_nlm_res,
+ .pc_argsize = sizeof(struct nlm_lockargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nlm_res_wrapper),
+ .pc_xdrressize = NLM3_nlm_res_sz,
+ .pc_name = "NM_LOCK",
},
- [NLMPROC_FREE_ALL] = {
- .pc_func = nlmsvc_proc_free_all,
- .pc_decode = nlmsvc_decode_notify,
- .pc_encode = nlmsvc_encode_void,
- .pc_argsize = sizeof(struct nlm_args),
- .pc_argzero = sizeof(struct nlm_args),
- .pc_ressize = sizeof(struct nlm_void),
- .pc_xdrressize = 0,
- .pc_name = "FREE_ALL",
+ [NLM_FREE_ALL] = {
+ .pc_func = nlmsvc_proc_free_all,
+ .pc_decode = nlm_svc_decode_nlm_notify,
+ .pc_encode = nlm_svc_encode_void,
+ .pc_argsize = sizeof(struct nlm_notify_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = 0,
+ .pc_xdrressize = XDR_void,
+ .pc_name = "FREE_ALL",
},
};
@@ -814,9 +1417,16 @@ static const struct svc_procedure nlmsvc_procedures[24] = {
* Storage requirements for XDR arguments and results
*/
union nlmsvc_xdrstore {
- struct nlm_args args;
- struct nlm_res res;
- struct nlm_reboot reboot;
+ struct nlm_testargs_wrapper testargs;
+ struct nlm_lockargs_wrapper lockargs;
+ struct nlm_cancargs_wrapper cancargs;
+ struct nlm_unlockargs_wrapper unlockargs;
+ struct nlm_notifyargs_wrapper notifyargs;
+ struct nlm_shareargs_wrapper shareargs;
+ struct nlm_notify_wrapper notify;
+ struct nlm_testres_wrapper testres;
+ struct nlm_res_wrapper res;
+ struct nlm_shareres_wrapper shareres;
};
/*
diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c
index 53f5655c128c..5ac0ec25d62d 100644
--- a/fs/lockd/svcshare.c
+++ b/fs/lockd/svcshare.c
@@ -19,7 +19,7 @@
#include "share.h"
static inline int
-nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh)
+nlm_cmp_owner(struct lockd_share *share, struct xdr_netobj *oh)
{
return share->s_owner.len == oh->len
&& !memcmp(share->s_owner.data, oh->data, oh->len);
@@ -39,7 +39,7 @@ __be32
nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh, u32 access, u32 mode)
{
- struct nlm_share *share;
+ struct lockd_share *share;
u8 *ohdata;
if (nlmsvc_file_cannot_lock(file))
@@ -85,7 +85,7 @@ __be32
nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh)
{
- struct nlm_share *share, **shpp;
+ struct lockd_share *share, **shpp;
if (nlmsvc_file_cannot_lock(file))
return nlm_lck_denied_nolocks;
@@ -111,7 +111,7 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
void nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file,
nlm_host_match_fn_t match)
{
- struct nlm_share *share, **shpp;
+ struct lockd_share *share, **shpp;
shpp = &file->f_shares;
while ((share = *shpp) != NULL) {
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index 9da9d6e0b42e..a0d1a6fbf61e 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -17,7 +17,6 @@
#include <linux/sunrpc/addr.h>
#include <linux/module.h>
#include <linux/mount.h>
-#include <uapi/linux/nfs2.h>
#include "lockd.h"
#include "share.h"
@@ -67,7 +66,7 @@ static inline unsigned int file_hash(struct nfs_fh *f)
{
unsigned int tmp=0;
int i;
- for (i=0; i<NFS2_FHSIZE;i++)
+ for (i = 0; i < LOCKD_FH_HASH_SIZE; i++)
tmp += f->data[i];
return tmp & (FILE_NRHASH - 1);
}
@@ -132,7 +131,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
*/
__be32
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
- struct nlm_lock *lock, int mode)
+ struct lockd_lock *lock, int mode)
{
struct nlm_file *file;
unsigned int hash;
@@ -150,6 +149,8 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
mutex_lock(&file->f_mutex);
nfserr = nlm_do_fopen(rqstp, file, mode);
mutex_unlock(&file->f_mutex);
+ if (nfserr)
+ goto out_unlock;
goto found;
}
nlm_debug_print_fh("creating file for", &lock->fh);
@@ -166,7 +167,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
nfserr = nlm_do_fopen(rqstp, file, mode);
if (nfserr)
- goto out_unlock;
+ goto out_free;
hlist_add_head(&file->f_list, &nlm_files[hash]);
diff --git a/fs/lockd/svcxdr.h b/fs/lockd/svcxdr.h
deleted file mode 100644
index 4f1a451da5ba..000000000000
--- a/fs/lockd/svcxdr.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Encode/decode NLM basic data types
- *
- * Basic NLMv3 XDR data types are not defined in an IETF standards
- * document. X/Open has a description of these data types that
- * is useful. See Chapter 10 of "Protocols for Interworking:
- * XNFS, Version 3W".
- *
- * Basic NLMv4 XDR data types are defined in Appendix II.1.4 of
- * RFC 1813: "NFS Version 3 Protocol Specification".
- *
- * Author: Chuck Lever <chuck.lever@oracle.com>
- *
- * Copyright (c) 2020, Oracle and/or its affiliates.
- */
-
-#ifndef _LOCKD_SVCXDR_H_
-#define _LOCKD_SVCXDR_H_
-
-static inline bool
-svcxdr_decode_stats(struct xdr_stream *xdr, __be32 *status)
-{
- __be32 *p;
-
- p = xdr_inline_decode(xdr, XDR_UNIT);
- if (!p)
- return false;
- *status = *p;
-
- return true;
-}
-
-static inline bool
-svcxdr_encode_stats(struct xdr_stream *xdr, __be32 status)
-{
- __be32 *p;
-
- p = xdr_reserve_space(xdr, XDR_UNIT);
- if (!p)
- return false;
- *p = status;
-
- return true;
-}
-
-static inline bool
-svcxdr_decode_string(struct xdr_stream *xdr, char **data, unsigned int *data_len)
-{
- __be32 *p;
- u32 len;
-
- if (xdr_stream_decode_u32(xdr, &len) < 0)
- return false;
- if (len > NLM_MAXSTRLEN)
- return false;
- p = xdr_inline_decode(xdr, len);
- if (!p)
- return false;
- *data_len = len;
- *data = (char *)p;
-
- return true;
-}
-
-/*
- * NLM cookies are defined by specification to be a variable-length
- * XDR opaque no longer than 1024 bytes. However, this implementation
- * limits their length to 32 bytes, and treats zero-length cookies
- * specially.
- */
-static inline bool
-svcxdr_decode_cookie(struct xdr_stream *xdr, struct nlm_cookie *cookie)
-{
- __be32 *p;
- u32 len;
-
- if (xdr_stream_decode_u32(xdr, &len) < 0)
- return false;
- if (len > NLM_MAXCOOKIELEN)
- return false;
- if (!len)
- goto out_hpux;
-
- p = xdr_inline_decode(xdr, len);
- if (!p)
- return false;
- cookie->len = len;
- memcpy(cookie->data, p, len);
-
- return true;
-
- /* apparently HPUX can return empty cookies */
-out_hpux:
- cookie->len = 4;
- memset(cookie->data, 0, 4);
- return true;
-}
-
-static inline bool
-svcxdr_encode_cookie(struct xdr_stream *xdr, const struct nlm_cookie *cookie)
-{
- __be32 *p;
-
- if (xdr_stream_encode_u32(xdr, cookie->len) < 0)
- return false;
- p = xdr_reserve_space(xdr, cookie->len);
- if (!p)
- return false;
- memcpy(p, cookie->data, cookie->len);
-
- return true;
-}
-
-static inline bool
-svcxdr_decode_owner(struct xdr_stream *xdr, struct xdr_netobj *obj)
-{
- __be32 *p;
- u32 len;
-
- if (xdr_stream_decode_u32(xdr, &len) < 0)
- return false;
- if (len > XDR_MAX_NETOBJ)
- return false;
- p = xdr_inline_decode(xdr, len);
- if (!p)
- return false;
- obj->len = len;
- obj->data = (u8 *)p;
-
- return true;
-}
-
-static inline bool
-svcxdr_encode_owner(struct xdr_stream *xdr, const struct xdr_netobj *obj)
-{
- if (obj->len > XDR_MAX_NETOBJ)
- return false;
- return xdr_stream_encode_opaque(xdr, obj->data, obj->len) > 0;
-}
-
-#endif /* _LOCKD_SVCXDR_H_ */
diff --git a/fs/lockd/trace.h b/fs/lockd/trace.h
index 7214d7e96a42..a11d04e8c835 100644
--- a/fs/lockd/trace.h
+++ b/fs/lockd/trace.h
@@ -48,7 +48,7 @@ NLM_STATUS_LIST
DECLARE_EVENT_CLASS(nlmclnt_lock_event,
TP_PROTO(
- const struct nlm_lock *lock,
+ const struct lockd_lock *lock,
const struct sockaddr *addr,
unsigned int addrlen,
__be32 status
@@ -61,8 +61,8 @@ DECLARE_EVENT_CLASS(nlmclnt_lock_event,
__field(u32, svid)
__field(u32, fh)
__field(unsigned long, status)
- __field(u64, start)
- __field(u64, len)
+ __field(loff_t, start)
+ __field(loff_t, end)
__sockaddr(addr, addrlen)
),
@@ -70,16 +70,16 @@ DECLARE_EVENT_CLASS(nlmclnt_lock_event,
__entry->oh = ~crc32_le(0xffffffff, lock->oh.data, lock->oh.len);
__entry->svid = lock->svid;
__entry->fh = nfs_fhandle_hash(&lock->fh);
- __entry->start = lock->lock_start;
- __entry->len = lock->lock_len;
+ __entry->start = lock->fl.fl_start;
+ __entry->end = lock->fl.fl_end;
__entry->status = be32_to_cpu(status);
__assign_sockaddr(addr, addr, addrlen);
),
TP_printk(
- "addr=%pISpc oh=0x%08x svid=0x%08x fh=0x%08x start=%llu len=%llu status=%s",
+ "addr=%pISpc oh=0x%08x svid=0x%08x fh=0x%08x start=%lld end=%lld status=%s",
__get_sockaddr(addr), __entry->oh, __entry->svid,
- __entry->fh, __entry->start, __entry->len,
+ __entry->fh, __entry->start, __entry->end,
show_nlm_status(__entry->status)
)
);
@@ -87,7 +87,7 @@ DECLARE_EVENT_CLASS(nlmclnt_lock_event,
#define DEFINE_NLMCLNT_EVENT(name) \
DEFINE_EVENT(nlmclnt_lock_event, name, \
TP_PROTO( \
- const struct nlm_lock *lock, \
+ const struct lockd_lock *lock, \
const struct sockaddr *addr, \
unsigned int addrlen, \
__be32 status \
diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c
deleted file mode 100644
index dfca8b8dab73..000000000000
--- a/fs/lockd/xdr.c
+++ /dev/null
@@ -1,354 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * linux/fs/lockd/xdr.c
- *
- * XDR support for lockd and the lock client.
- *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
- */
-
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/nfs.h>
-
-#include <linux/sunrpc/xdr.h>
-#include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/sunrpc/stats.h>
-
-#include <uapi/linux/nfs2.h>
-
-#include "lockd.h"
-#include "share.h"
-#include "svcxdr.h"
-
-static inline loff_t
-s32_to_loff_t(__s32 offset)
-{
- return (loff_t)offset;
-}
-
-static inline __s32
-loff_t_to_s32(loff_t offset)
-{
- __s32 res;
- if (offset >= NLM_OFFSET_MAX)
- res = NLM_OFFSET_MAX;
- else if (offset <= -NLM_OFFSET_MAX)
- res = -NLM_OFFSET_MAX;
- else
- res = offset;
- return res;
-}
-
-/*
- * NLM file handles are defined by specification to be a variable-length
- * XDR opaque no longer than 1024 bytes. However, this implementation
- * constrains their length to exactly the length of an NFSv2 file
- * handle.
- */
-static bool
-svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
-{
- __be32 *p;
- u32 len;
-
- if (xdr_stream_decode_u32(xdr, &len) < 0)
- return false;
- if (len != NFS2_FHSIZE)
- return false;
-
- p = xdr_inline_decode(xdr, len);
- if (!p)
- return false;
- fh->size = NFS2_FHSIZE;
- memcpy(fh->data, p, len);
- memset(fh->data + NFS2_FHSIZE, 0, sizeof(fh->data) - NFS2_FHSIZE);
-
- return true;
-}
-
-static bool
-svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock)
-{
- struct file_lock *fl = &lock->fl;
- s32 start, len, end;
-
- if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
- return false;
- if (!svcxdr_decode_fhandle(xdr, &lock->fh))
- return false;
- if (!svcxdr_decode_owner(xdr, &lock->oh))
- return false;
- if (xdr_stream_decode_u32(xdr, &lock->svid) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &start) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &len) < 0)
- return false;
-
- locks_init_lock(fl);
- fl->c.flc_flags = FL_POSIX;
- fl->c.flc_type = F_RDLCK;
- end = start + len - 1;
- fl->fl_start = s32_to_loff_t(start);
- if (len == 0 || end < 0)
- fl->fl_end = OFFSET_MAX;
- else
- fl->fl_end = s32_to_loff_t(end);
-
- return true;
-}
-
-static bool
-svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock)
-{
- const struct file_lock *fl = &lock->fl;
- s32 start, len;
-
- /* exclusive */
- if (xdr_stream_encode_bool(xdr, fl->c.flc_type != F_RDLCK) < 0)
- return false;
- if (xdr_stream_encode_u32(xdr, lock->svid) < 0)
- return false;
- if (!svcxdr_encode_owner(xdr, &lock->oh))
- return false;
- start = loff_t_to_s32(fl->fl_start);
- if (fl->fl_end == OFFSET_MAX)
- len = 0;
- else
- len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
- if (xdr_stream_encode_u32(xdr, start) < 0)
- return false;
- if (xdr_stream_encode_u32(xdr, len) < 0)
- return false;
-
- return true;
-}
-
-static bool
-svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp)
-{
- if (!svcxdr_encode_stats(xdr, resp->status))
- return false;
- switch (resp->status) {
- case nlm_lck_denied:
- if (!svcxdr_encode_holder(xdr, &resp->lock))
- return false;
- }
-
- return true;
-}
-
-
-/*
- * Decode Call arguments
- */
-
-bool
-nlmsvc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- return true;
-}
-
-bool
-nlmsvc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_args *argp = rqstp->rq_argp;
- u32 exclusive;
-
- if (!svcxdr_decode_cookie(xdr, &argp->cookie))
- return false;
- if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
- return false;
- if (!svcxdr_decode_lock(xdr, &argp->lock))
- return false;
- if (exclusive)
- argp->lock.fl.c.flc_type = F_WRLCK;
-
- return true;
-}
-
-bool
-nlmsvc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_args *argp = rqstp->rq_argp;
- u32 exclusive;
-
- if (!svcxdr_decode_cookie(xdr, &argp->cookie))
- return false;
- if (xdr_stream_decode_bool(xdr, &argp->block) < 0)
- return false;
- if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
- return false;
- if (!svcxdr_decode_lock(xdr, &argp->lock))
- return false;
- if (exclusive)
- argp->lock.fl.c.flc_type = F_WRLCK;
- if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
- return false;
- argp->monitor = 1; /* monitor client by default */
-
- return true;
-}
-
-bool
-nlmsvc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_args *argp = rqstp->rq_argp;
- u32 exclusive;
-
- if (!svcxdr_decode_cookie(xdr, &argp->cookie))
- return false;
- if (xdr_stream_decode_bool(xdr, &argp->block) < 0)
- return false;
- if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
- return false;
- if (!svcxdr_decode_lock(xdr, &argp->lock))
- return false;
- if (exclusive)
- argp->lock.fl.c.flc_type = F_WRLCK;
-
- return true;
-}
-
-bool
-nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_args *argp = rqstp->rq_argp;
-
- if (!svcxdr_decode_cookie(xdr, &argp->cookie))
- return false;
- if (!svcxdr_decode_lock(xdr, &argp->lock))
- return false;
- argp->lock.fl.c.flc_type = F_UNLCK;
-
- return true;
-}
-
-bool
-nlmsvc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_res *resp = rqstp->rq_argp;
-
- if (!svcxdr_decode_cookie(xdr, &resp->cookie))
- return false;
- if (!svcxdr_decode_stats(xdr, &resp->status))
- return false;
-
- return true;
-}
-
-bool
-nlmsvc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_reboot *argp = rqstp->rq_argp;
- __be32 *p;
- u32 len;
-
- if (xdr_stream_decode_u32(xdr, &len) < 0)
- return false;
- if (len > SM_MAXSTRLEN)
- return false;
- p = xdr_inline_decode(xdr, len);
- if (!p)
- return false;
- argp->len = len;
- argp->mon = (char *)p;
- if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
- return false;
- p = xdr_inline_decode(xdr, SM_PRIV_SIZE);
- if (!p)
- return false;
- memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
-
- return true;
-}
-
-bool
-nlmsvc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_lock *lock = &argp->lock;
-
- memset(lock, 0, sizeof(*lock));
- locks_init_lock(&lock->fl);
- lock->svid = LOCKD_SHARE_SVID;
-
- if (!svcxdr_decode_cookie(xdr, &argp->cookie))
- return false;
- if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
- return false;
- if (!svcxdr_decode_fhandle(xdr, &lock->fh))
- return false;
- if (!svcxdr_decode_owner(xdr, &lock->oh))
- return false;
- /* XXX: Range checks are missing in the original code */
- if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0)
- return false;
-
- return true;
-}
-
-bool
-nlmsvc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_args *argp = rqstp->rq_argp;
- struct nlm_lock *lock = &argp->lock;
-
- if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
- return false;
- if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
- return false;
-
- return true;
-}
-
-
-/*
- * Encode Reply results
- */
-
-bool
-nlmsvc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- return true;
-}
-
-bool
-nlmsvc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_res *resp = rqstp->rq_resp;
-
- return svcxdr_encode_cookie(xdr, &resp->cookie) &&
- svcxdr_encode_testrply(xdr, resp);
-}
-
-bool
-nlmsvc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_res *resp = rqstp->rq_resp;
-
- return svcxdr_encode_cookie(xdr, &resp->cookie) &&
- svcxdr_encode_stats(xdr, resp->status);
-}
-
-bool
-nlmsvc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nlm_res *resp = rqstp->rq_resp;
-
- if (!svcxdr_encode_cookie(xdr, &resp->cookie))
- return false;
- if (!svcxdr_encode_stats(xdr, resp->status))
- return false;
- /* sequence */
- if (xdr_stream_encode_u32(xdr, 0) < 0)
- return false;
-
- return true;
-}
diff --git a/fs/lockd/xdr.h b/fs/lockd/xdr.h
index 3c60817c4349..a1126cca98c6 100644
--- a/fs/lockd/xdr.h
+++ b/fs/lockd/xdr.h
@@ -20,8 +20,6 @@ struct nsm_private {
unsigned char data[SM_PRIV_SIZE];
};
-struct svc_rqst;
-
#define NLM_MAXCOOKIELEN 32
#define NLM_MAXSTRLEN 1024
@@ -32,7 +30,7 @@ struct svc_rqst;
#define nlm_lck_denied_grace_period cpu_to_be32(NLM_LCK_DENIED_GRACE_PERIOD)
/* Lock info passed via NLM */
-struct nlm_lock {
+struct lockd_lock {
char * caller;
unsigned int len; /* length of "caller" */
struct nfs_fh fh;
@@ -49,8 +47,7 @@ struct nlm_lock {
* 32 bytes.
*/
-struct nlm_cookie
-{
+struct lockd_cookie {
unsigned char data[NLM_MAXCOOKIELEN];
unsigned int len;
};
@@ -58,49 +55,31 @@ struct nlm_cookie
/*
* Generic lockd arguments for all but sm_notify
*/
-struct nlm_args {
- struct nlm_cookie cookie;
- struct nlm_lock lock;
+struct lockd_args {
+ struct lockd_cookie cookie;
+ struct lockd_lock lock;
u32 block;
u32 reclaim;
u32 state;
- u32 monitor;
- u32 fsm_access;
- u32 fsm_mode;
};
/*
* Generic lockd result
*/
-struct nlm_res {
- struct nlm_cookie cookie;
+struct lockd_res {
+ struct lockd_cookie cookie;
__be32 status;
- struct nlm_lock lock;
+ struct lockd_lock lock;
};
/*
* statd callback when client has rebooted
*/
-struct nlm_reboot {
+struct lockd_reboot {
char *mon;
unsigned int len;
u32 state;
struct nsm_private priv;
};
-bool nlmsvc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-
-bool nlmsvc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nlmsvc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-
#endif /* _LOCKD_XDR_H */
diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c
index 2ed2126201f4..acd3511c04a6 100644
--- a/fs/nfs/dns_resolve.c
+++ b/fs/nfs/dns_resolve.c
@@ -156,7 +156,7 @@ static int nfs_dns_upcall(struct cache_detail *cd,
if (!nfs_cache_upcall(cd, key->hostname))
return 0;
clear_bit(CACHE_PENDING, &ch->flags);
- return sunrpc_cache_pipe_upcall_timeout(cd, ch);
+ return sunrpc_cache_upcall_warn(cd, ch);
}
static int nfs_dns_match(struct cache_head *ca,
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 35fef3197a66..a47c90f40422 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -17,6 +17,8 @@
#include <linux/module.h>
#include <linux/exportfs.h>
#include <linux/sunrpc/svc_xprt.h>
+#include <net/genetlink.h>
+#include <uapi/linux/nfsd_netlink.h>
#include "nfsd.h"
#include "nfsfh.h"
@@ -24,6 +26,7 @@
#include "pnfs.h"
#include "filecache.h"
#include "trace.h"
+#include "netlink.h"
#define NFSDDBG_FACILITY NFSDDBG_EXPORT
@@ -36,35 +39,24 @@
* second map contains a reference to the entry in the first map.
*/
-static struct workqueue_struct *nfsd_export_wq;
-
#define EXPKEY_HASHBITS 8
#define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS)
#define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1)
-static void expkey_release(struct work_struct *work)
+static void expkey_put(struct kref *ref)
{
- struct svc_expkey *key = container_of(to_rcu_work(work),
- struct svc_expkey, ek_rwork);
+ struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
if (test_bit(CACHE_VALID, &key->h.flags) &&
!test_bit(CACHE_NEGATIVE, &key->h.flags))
path_put(&key->ek_path);
auth_domain_put(key->ek_client);
- kfree(key);
-}
-
-static void expkey_put(struct kref *ref)
-{
- struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
-
- INIT_RCU_WORK(&key->ek_rwork, expkey_release);
- queue_rcu_work(nfsd_export_wq, &key->ek_rwork);
+ kfree_rcu(key, ek_rcu);
}
static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall(cd, h);
+ return sunrpc_cache_upcall(cd, h);
}
static void expkey_request(struct cache_detail *cd,
@@ -263,12 +255,18 @@ static void expkey_flush(void)
mutex_unlock(&nfsd_mutex);
}
+static int expkey_notify(struct cache_detail *cd, struct cache_head *h)
+{
+ return nfsd_cache_notify(cd, h, NFSD_CACHE_TYPE_EXPKEY);
+}
+
static const struct cache_detail svc_expkey_cache_template = {
.owner = THIS_MODULE,
.hash_size = EXPKEY_HASHMAX,
.name = "nfsd.fh",
.cache_put = expkey_put,
.cache_upcall = expkey_upcall,
+ .cache_notify = expkey_notify,
.cache_request = expkey_request,
.cache_parse = expkey_parse,
.cache_show = expkey_show,
@@ -319,6 +317,266 @@ svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
return NULL;
}
+/**
+ * nfsd_nl_expkey_get_reqs_dumpit - dump pending expkey requests
+ * @skb: reply buffer
+ * @cb: netlink metadata and command arguments
+ *
+ * Walk the expkey cache's pending request list and create a netlink
+ * message with a nested entry for each cache_request, containing the
+ * seqno, client string, fsidtype and fsid.
+ *
+ * Uses cb->args[0] as a seqno cursor for dump continuation across
+ * multiple netlink messages.
+ *
+ * Returns the size of the reply or a negative errno.
+ */
+int nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct nfsd_net *nn;
+ struct cache_detail *cd;
+ struct cache_head **items;
+ u64 *seqnos;
+ int cnt, i, emitted;
+ void *hdr;
+ int ret;
+
+ nn = net_generic(sock_net(skb->sk), nfsd_net_id);
+
+ mutex_lock(&nfsd_mutex);
+
+ cd = nn->svc_expkey_cache;
+ if (!cd) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ cnt = sunrpc_cache_requests_count(cd);
+ if (!cnt) {
+ ret = 0;
+ goto out_unlock;
+ }
+
+ items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
+ if (!items || !seqnos) {
+ ret = -ENOMEM;
+ goto out_alloc;
+ }
+
+ cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt,
+ cb->args[0]);
+ if (!cnt) {
+ ret = 0;
+ goto out_alloc;
+ }
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &nfsd_nl_family,
+ NLM_F_MULTI, NFSD_CMD_EXPKEY_GET_REQS);
+ if (!hdr) {
+ ret = -ENOBUFS;
+ goto out_put;
+ }
+
+ emitted = 0;
+ for (i = 0; i < cnt; i++) {
+ struct svc_expkey *ek;
+ struct nlattr *nest;
+
+ ek = container_of(items[i], struct svc_expkey, h);
+
+ nest = nla_nest_start(skb, NFSD_A_EXPKEY_REQS_REQUESTS);
+ if (!nest)
+ break;
+
+ if (nla_put_u64_64bit(skb, NFSD_A_EXPKEY_SEQNO,
+ seqnos[i], 0) ||
+ nla_put_string(skb, NFSD_A_EXPKEY_CLIENT,
+ ek->ek_client->name) ||
+ nla_put_u8(skb, NFSD_A_EXPKEY_FSIDTYPE,
+ ek->ek_fsidtype) ||
+ nla_put(skb, NFSD_A_EXPKEY_FSID,
+ key_len(ek->ek_fsidtype), ek->ek_fsid)) {
+ nla_nest_cancel(skb, nest);
+ break;
+ }
+
+ nla_nest_end(skb, nest);
+ cb->args[0] = seqnos[i];
+ emitted++;
+ }
+
+ if (!emitted) {
+ genlmsg_cancel(skb, hdr);
+ ret = -EMSGSIZE;
+ goto out_put;
+ }
+
+ genlmsg_end(skb, hdr);
+ ret = skb->len;
+out_put:
+ for (i = 0; i < cnt; i++)
+ cache_put(items[i], cd);
+out_alloc:
+ kfree(seqnos);
+ kfree(items);
+out_unlock:
+ mutex_unlock(&nfsd_mutex);
+ return ret;
+}
+
+/**
+ * nfsd_nl_parse_one_expkey - parse one expkey entry from netlink
+ * @cd: cache_detail for the expkey cache
+ * @attr: nested attribute containing expkey fields
+ *
+ * Parses one expkey entry from a netlink message and updates the
+ * cache. Mirrors the logic in expkey_parse().
+ *
+ * Returns 0 on success or a negative errno.
+ */
+static int nfsd_nl_parse_one_expkey(struct cache_detail *cd,
+ struct nlattr *attr)
+{
+ struct nlattr *tb[NFSD_A_EXPKEY_PATH + 1];
+ struct auth_domain *dom = NULL;
+ struct svc_expkey key;
+ struct svc_expkey *ek = NULL;
+ struct timespec64 boot;
+ int err;
+ u8 fsidtype;
+ int fsid_len;
+
+ err = nla_parse_nested(tb, NFSD_A_EXPKEY_PATH, attr,
+ nfsd_expkey_nl_policy, NULL);
+ if (err)
+ return err;
+
+ /* client (required) */
+ if (!tb[NFSD_A_EXPKEY_CLIENT])
+ return -EINVAL;
+
+ dom = auth_domain_find(nla_data(tb[NFSD_A_EXPKEY_CLIENT]));
+ if (!dom)
+ return -ENOENT;
+
+ /* fsidtype (required) */
+ if (!tb[NFSD_A_EXPKEY_FSIDTYPE]) {
+ err = -EINVAL;
+ goto out_dom;
+ }
+ fsidtype = nla_get_u8(tb[NFSD_A_EXPKEY_FSIDTYPE]);
+ if (key_len(fsidtype) == 0) {
+ err = -EINVAL;
+ goto out_dom;
+ }
+
+ /* fsid (required) */
+ if (!tb[NFSD_A_EXPKEY_FSID]) {
+ err = -EINVAL;
+ goto out_dom;
+ }
+ fsid_len = nla_len(tb[NFSD_A_EXPKEY_FSID]);
+ if (fsid_len != key_len(fsidtype)) {
+ err = -EINVAL;
+ goto out_dom;
+ }
+
+ /* expiry (required, wallclock seconds) */
+ if (!tb[NFSD_A_EXPKEY_EXPIRY]) {
+ err = -EINVAL;
+ goto out_dom;
+ }
+
+ key.h.flags = 0;
+ getboottime64(&boot);
+ key.h.expiry_time = nla_get_u64(tb[NFSD_A_EXPKEY_EXPIRY]) -
+ boot.tv_sec;
+ key.ek_client = dom;
+ key.ek_fsidtype = fsidtype;
+ memcpy(key.ek_fsid, nla_data(tb[NFSD_A_EXPKEY_FSID]), fsid_len);
+
+ ek = svc_expkey_lookup(cd, &key);
+ if (!ek) {
+ err = -ENOMEM;
+ goto out_dom;
+ }
+
+ if (tb[NFSD_A_EXPKEY_NEGATIVE]) {
+ set_bit(CACHE_NEGATIVE, &key.h.flags);
+ ek = svc_expkey_update(cd, &key, ek);
+ if (ek)
+ trace_nfsd_expkey_update(ek, NULL);
+ else
+ err = -ENOMEM;
+ } else if (tb[NFSD_A_EXPKEY_PATH]) {
+ err = kern_path(nla_data(tb[NFSD_A_EXPKEY_PATH]), 0,
+ &key.ek_path);
+ if (err)
+ goto out_ek;
+ ek = svc_expkey_update(cd, &key, ek);
+ if (ek)
+ trace_nfsd_expkey_update(ek,
+ nla_data(tb[NFSD_A_EXPKEY_PATH]));
+ else
+ err = -ENOMEM;
+ path_put(&key.ek_path);
+ } else {
+ err = -EINVAL;
+ goto out_ek;
+ }
+
+ cache_flush();
+
+out_ek:
+ if (ek)
+ cache_put(&ek->h, cd);
+out_dom:
+ auth_domain_put(dom);
+ return err;
+}
+
+/**
+ * nfsd_nl_expkey_set_reqs_doit - respond to expkey requests
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Parse one or more expkey cache responses from userspace and
+ * update the expkey cache accordingly.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int nfsd_nl_expkey_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct nfsd_net *nn;
+ struct cache_detail *cd;
+ const struct nlattr *attr;
+ int rem, ret = 0;
+
+ nn = net_generic(genl_info_net(info), nfsd_net_id);
+
+ mutex_lock(&nfsd_mutex);
+
+ cd = nn->svc_expkey_cache;
+ if (!cd) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ nlmsg_for_each_attr_type(attr, NFSD_A_EXPKEY_REQS_REQUESTS,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ ret = nfsd_nl_parse_one_expkey(cd, (struct nlattr *)attr);
+ if (ret)
+ break;
+ }
+
+out_unlock:
+ mutex_unlock(&nfsd_mutex);
+ return ret;
+}
#define EXPORT_HASHBITS 8
#define EXPORT_HASHMAX (1<< EXPORT_HASHBITS)
@@ -364,13 +622,11 @@ static void export_stats_destroy(struct export_stats *stats)
EXP_STATS_COUNTERS_NUM);
}
-static void svc_export_release(struct work_struct *work)
+static void svc_export_release(struct rcu_head *rcu_head)
{
- struct svc_export *exp = container_of(to_rcu_work(work),
- struct svc_export, ex_rwork);
+ struct svc_export *exp = container_of(rcu_head, struct svc_export,
+ ex_rcu);
- path_put(&exp->ex_path);
- auth_domain_put(exp->ex_client);
nfsd4_fslocs_free(&exp->ex_fslocs);
export_stats_destroy(exp->ex_stats);
kfree(exp->ex_stats);
@@ -382,13 +638,450 @@ static void svc_export_put(struct kref *ref)
{
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
- INIT_RCU_WORK(&exp->ex_rwork, svc_export_release);
- queue_rcu_work(nfsd_export_wq, &exp->ex_rwork);
+ path_put(&exp->ex_path);
+ auth_domain_put(exp->ex_client);
+ call_rcu(&exp->ex_rcu, svc_export_release);
+}
+
+/**
+ * nfsd_nl_svc_export_get_reqs_dumpit - dump pending svc_export requests
+ * @skb: reply buffer
+ * @cb: netlink metadata and command arguments
+ *
+ * Walk the svc_export cache's pending request list and create a netlink
+ * message with a nested entry for each cache_request, containing the
+ * seqno, client string, and path.
+ *
+ * Uses cb->args[0] as a seqno cursor for dump continuation across
+ * multiple netlink messages.
+ *
+ * Returns the size of the reply or a negative errno.
+ */
+int nfsd_nl_svc_export_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct nfsd_net *nn;
+ struct cache_detail *cd;
+ struct cache_head **items;
+ u64 *seqnos;
+ int cnt, i, emitted;
+ char *pathbuf;
+ void *hdr;
+ int ret;
+
+ nn = net_generic(sock_net(skb->sk), nfsd_net_id);
+
+ mutex_lock(&nfsd_mutex);
+
+ cd = nn->svc_export_cache;
+ if (!cd) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ cnt = sunrpc_cache_requests_count(cd);
+ if (!cnt) {
+ ret = 0;
+ goto out_unlock;
+ }
+
+ items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
+ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (!items || !seqnos || !pathbuf) {
+ ret = -ENOMEM;
+ goto out_alloc;
+ }
+
+ cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt,
+ cb->args[0]);
+ if (!cnt) {
+ ret = 0;
+ goto out_alloc;
+ }
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &nfsd_nl_family,
+ NLM_F_MULTI, NFSD_CMD_SVC_EXPORT_GET_REQS);
+ if (!hdr) {
+ ret = -ENOBUFS;
+ goto out_put;
+ }
+
+ emitted = 0;
+ for (i = 0; i < cnt; i++) {
+ struct svc_export *exp;
+ struct nlattr *nest;
+ char *pth;
+
+ exp = container_of(items[i], struct svc_export, h);
+
+ pth = d_path(&exp->ex_path, pathbuf, PATH_MAX);
+ if (IS_ERR(pth))
+ continue;
+
+ nest = nla_nest_start(skb,
+ NFSD_A_SVC_EXPORT_REQS_REQUESTS);
+ if (!nest)
+ break;
+
+ if (nla_put_u64_64bit(skb, NFSD_A_SVC_EXPORT_SEQNO,
+ seqnos[i], 0) ||
+ nla_put_string(skb, NFSD_A_SVC_EXPORT_CLIENT,
+ exp->ex_client->name) ||
+ nla_put_string(skb, NFSD_A_SVC_EXPORT_PATH, pth)) {
+ nla_nest_cancel(skb, nest);
+ break;
+ }
+
+ nla_nest_end(skb, nest);
+ cb->args[0] = seqnos[i];
+ emitted++;
+ }
+
+ if (!emitted) {
+ genlmsg_cancel(skb, hdr);
+ ret = -EMSGSIZE;
+ goto out_put;
+ }
+
+ genlmsg_end(skb, hdr);
+ ret = skb->len;
+out_put:
+ for (i = 0; i < cnt; i++)
+ cache_put(items[i], cd);
+out_alloc:
+ kfree(pathbuf);
+ kfree(seqnos);
+ kfree(items);
+out_unlock:
+ mutex_unlock(&nfsd_mutex);
+ return ret;
+}
+
+/**
+ * nfsd_nl_parse_fslocations - parse fslocations from netlink
+ * @attr: NFSD_A_SVC_EXPORT_FSLOCATIONS nested attribute
+ * @fsloc: fslocations struct to fill in
+ *
+ * Returns 0 on success or a negative errno.
+ */
+static int nfsd_nl_parse_fslocations(struct nlattr *attr,
+ struct nfsd4_fs_locations *fsloc)
+{
+ struct nlattr *loc_attr;
+ int rem, count = 0;
+ int err;
+
+ if (fsloc->locations)
+ return -EINVAL;
+
+ /* Count locations first */
+ nla_for_each_nested_type(loc_attr, NFSD_A_FSLOCATIONS_LOCATION,
+ attr, rem)
+ count++;
+
+ if (count > MAX_FS_LOCATIONS)
+ return -EINVAL;
+ if (!count)
+ return 0;
+
+ fsloc->locations = kcalloc(count, sizeof(struct nfsd4_fs_location),
+ GFP_KERNEL);
+ if (!fsloc->locations)
+ return -ENOMEM;
+
+ nla_for_each_nested_type(loc_attr, NFSD_A_FSLOCATIONS_LOCATION,
+ attr, rem) {
+ struct nlattr *tb[NFSD_A_FSLOCATION_PATH + 1];
+ struct nfsd4_fs_location *loc;
+
+ err = nla_parse_nested(tb, NFSD_A_FSLOCATION_PATH, loc_attr,
+ nfsd_fslocation_nl_policy, NULL);
+ if (err)
+ goto out_free;
+
+ if (!tb[NFSD_A_FSLOCATION_HOST] ||
+ !tb[NFSD_A_FSLOCATION_PATH]) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ loc = &fsloc->locations[fsloc->locations_count++];
+ loc->hosts = kstrdup(nla_data(tb[NFSD_A_FSLOCATION_HOST]),
+ GFP_KERNEL);
+ loc->path = kstrdup(nla_data(tb[NFSD_A_FSLOCATION_PATH]),
+ GFP_KERNEL);
+ if (!loc->hosts || !loc->path) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ }
+
+ return 0;
+out_free:
+ nfsd4_fslocs_free(fsloc);
+ return err;
+}
+
+static struct svc_export *svc_export_update(struct svc_export *new,
+ struct svc_export *old);
+static struct svc_export *svc_export_lookup(struct svc_export *);
+static int check_export(const struct path *path, int *flags,
+ unsigned char *uuid);
+
+/**
+ * nfsd_nl_parse_one_export - parse one svc_export entry from a netlink message
+ * @cd: cache_detail for the svc_export cache
+ * @attr: nested attribute containing svc-export fields
+ *
+ * Parses one svc-export entry from a netlink message and updates the
+ * cache. Mirrors the logic in svc_export_parse().
+ *
+ * Returns 0 on success or a negative errno.
+ */
+static int nfsd_nl_parse_one_export(struct cache_detail *cd,
+ struct nlattr *attr)
+{
+ struct nlattr *tb[NFSD_A_SVC_EXPORT_FSID + 1];
+ struct auth_domain *dom = NULL;
+ struct svc_export exp = {}, *expp;
+ struct nlattr *secinfo_attr;
+ struct timespec64 boot;
+ int err, rem;
+
+ err = nla_parse_nested(tb, NFSD_A_SVC_EXPORT_FSID, attr,
+ nfsd_svc_export_nl_policy, NULL);
+ if (err)
+ return err;
+
+ /* client (required) */
+ if (!tb[NFSD_A_SVC_EXPORT_CLIENT])
+ return -EINVAL;
+
+ dom = auth_domain_find(nla_data(tb[NFSD_A_SVC_EXPORT_CLIENT]));
+ if (!dom)
+ return -ENOENT;
+
+ /* path (required) */
+ if (!tb[NFSD_A_SVC_EXPORT_PATH]) {
+ err = -EINVAL;
+ goto out_dom;
+ }
+
+ err = kern_path(nla_data(tb[NFSD_A_SVC_EXPORT_PATH]), 0,
+ &exp.ex_path);
+ if (err)
+ goto out_dom;
+
+ exp.ex_client = dom;
+ exp.cd = cd;
+ exp.ex_devid_map = NULL;
+ exp.ex_xprtsec_modes = NFSEXP_XPRTSEC_ALL;
+
+ /* expiry (required, wallclock seconds) */
+ if (!tb[NFSD_A_SVC_EXPORT_EXPIRY]) {
+ err = -EINVAL;
+ goto out_path;
+ }
+ getboottime64(&boot);
+ exp.h.expiry_time = nla_get_u64(tb[NFSD_A_SVC_EXPORT_EXPIRY]) -
+ boot.tv_sec;
+
+ if (tb[NFSD_A_SVC_EXPORT_NEGATIVE]) {
+ set_bit(CACHE_NEGATIVE, &exp.h.flags);
+ } else {
+ /* flags */
+ if (tb[NFSD_A_SVC_EXPORT_FLAGS])
+ exp.ex_flags = nla_get_u32(tb[NFSD_A_SVC_EXPORT_FLAGS]);
+
+ /* anon uid */
+ if (tb[NFSD_A_SVC_EXPORT_ANON_UID]) {
+ u32 uid = nla_get_u32(tb[NFSD_A_SVC_EXPORT_ANON_UID]);
+
+ exp.ex_anon_uid = make_kuid(current_user_ns(), uid);
+ }
+
+ /* anon gid */
+ if (tb[NFSD_A_SVC_EXPORT_ANON_GID]) {
+ u32 gid = nla_get_u32(tb[NFSD_A_SVC_EXPORT_ANON_GID]);
+
+ exp.ex_anon_gid = make_kgid(current_user_ns(), gid);
+ }
+
+ /* fsid */
+ if (tb[NFSD_A_SVC_EXPORT_FSID])
+ exp.ex_fsid = nla_get_s32(tb[NFSD_A_SVC_EXPORT_FSID]);
+
+ /* fslocations */
+ if (tb[NFSD_A_SVC_EXPORT_FSLOCATIONS]) {
+ struct nlattr *fsl = tb[NFSD_A_SVC_EXPORT_FSLOCATIONS];
+
+ err = nfsd_nl_parse_fslocations(fsl,
+ &exp.ex_fslocs);
+ if (err)
+ goto out_path;
+ }
+
+ /* uuid */
+ if (tb[NFSD_A_SVC_EXPORT_UUID]) {
+ if (nla_len(tb[NFSD_A_SVC_EXPORT_UUID]) !=
+ EX_UUID_LEN) {
+ err = -EINVAL;
+ goto out_fslocs;
+ }
+ exp.ex_uuid = kmemdup(nla_data(tb[NFSD_A_SVC_EXPORT_UUID]),
+ EX_UUID_LEN, GFP_KERNEL);
+ if (!exp.ex_uuid) {
+ err = -ENOMEM;
+ goto out_fslocs;
+ }
+ }
+
+ /* secinfo (multi-attr) */
+ nla_for_each_nested_type(secinfo_attr,
+ NFSD_A_SVC_EXPORT_SECINFO,
+ attr, rem) {
+ struct nlattr *ftb[NFSD_A_AUTH_FLAVOR_FLAGS + 1];
+ struct exp_flavor_info *f;
+
+ if (exp.ex_nflavors >= MAX_SECINFO_LIST) {
+ err = -EINVAL;
+ goto out_uuid;
+ }
+
+ err = nla_parse_nested(ftb,
+ NFSD_A_AUTH_FLAVOR_FLAGS,
+ secinfo_attr,
+ nfsd_auth_flavor_nl_policy,
+ NULL);
+ if (err)
+ goto out_uuid;
+
+ f = &exp.ex_flavors[exp.ex_nflavors++];
+
+ if (ftb[NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR])
+ f->pseudoflavor = nla_get_u32(ftb[NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR]);
+
+ if (ftb[NFSD_A_AUTH_FLAVOR_FLAGS])
+ f->flags = nla_get_u32(ftb[NFSD_A_AUTH_FLAVOR_FLAGS]);
+
+ /* Only some flags are allowed to differ between flavors: */
+ if (~NFSEXP_SECINFO_FLAGS & (f->flags ^ exp.ex_flags)) {
+ err = -EINVAL;
+ goto out_uuid;
+ }
+ }
+
+ /* xprtsec (multi-attr u32) */
+ if (tb[NFSD_A_SVC_EXPORT_XPRTSEC]) {
+ struct nlattr *xp_attr;
+
+ exp.ex_xprtsec_modes = 0;
+ nla_for_each_nested_type(xp_attr,
+ NFSD_A_SVC_EXPORT_XPRTSEC,
+ attr, rem) {
+ u32 mode = nla_get_u32(xp_attr);
+
+ if (mode > NFSEXP_XPRTSEC_MTLS) {
+ err = -EINVAL;
+ goto out_uuid;
+ }
+ exp.ex_xprtsec_modes |= mode;
+ }
+ }
+
+ err = check_export(&exp.ex_path, &exp.ex_flags,
+ exp.ex_uuid);
+ if (err)
+ goto out_uuid;
+
+ if (exp.h.expiry_time < seconds_since_boot())
+ goto out_uuid;
+
+ err = -EINVAL;
+ if (!uid_valid(exp.ex_anon_uid))
+ goto out_uuid;
+ if (!gid_valid(exp.ex_anon_gid))
+ goto out_uuid;
+ err = 0;
+
+ nfsd4_setup_layout_type(&exp);
+ }
+
+ expp = svc_export_lookup(&exp);
+ if (!expp) {
+ err = -ENOMEM;
+ goto out_uuid;
+ }
+ expp = svc_export_update(&exp, expp);
+ if (expp) {
+ trace_nfsd_export_update(expp);
+ cache_flush();
+ exp_put(expp);
+ } else {
+ err = -ENOMEM;
+ }
+
+out_uuid:
+ kfree(exp.ex_uuid);
+out_fslocs:
+ nfsd4_fslocs_free(&exp.ex_fslocs);
+out_path:
+ path_put(&exp.ex_path);
+out_dom:
+ auth_domain_put(dom);
+ return err;
+}
+
+/**
+ * nfsd_nl_svc_export_set_reqs_doit - respond to svc_export requests
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Parse one or more svc_export cache responses from userspace and
+ * update the export cache accordingly.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int nfsd_nl_svc_export_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct nfsd_net *nn;
+ struct cache_detail *cd;
+ const struct nlattr *attr;
+ int rem, ret = 0;
+
+ nn = net_generic(genl_info_net(info), nfsd_net_id);
+
+ mutex_lock(&nfsd_mutex);
+
+ cd = nn->svc_export_cache;
+ if (!cd) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ nlmsg_for_each_attr_type(attr, NFSD_A_SVC_EXPORT_REQS_REQUESTS,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ ret = nfsd_nl_parse_one_export(cd, (struct nlattr *)attr);
+ if (ret)
+ break;
+ }
+
+out_unlock:
+ mutex_unlock(&nfsd_mutex);
+ return ret;
}
static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall(cd, h);
+ return sunrpc_cache_upcall(cd, h);
+}
+
+static int svc_export_notify(struct cache_detail *cd, struct cache_head *h)
+{
+ return nfsd_cache_notify(cd, h, NFSD_CACHE_TYPE_SVC_EXPORT);
}
static void svc_export_request(struct cache_detail *cd,
@@ -410,10 +1103,6 @@ static void svc_export_request(struct cache_detail *cd,
(*bpp)[-1] = '\n';
}
-static struct svc_export *svc_export_update(struct svc_export *new,
- struct svc_export *old);
-static struct svc_export *svc_export_lookup(struct svc_export *);
-
static int check_export(const struct path *path, int *flags, unsigned char *uuid)
{
struct inode *inode = d_inode(path->dentry);
@@ -908,6 +1597,7 @@ static const struct cache_detail svc_export_cache_template = {
.name = "nfsd.export",
.cache_put = svc_export_put,
.cache_upcall = svc_export_upcall,
+ .cache_notify = svc_export_notify,
.cache_request = svc_export_request,
.cache_parse = svc_export_parse,
.cache_show = svc_export_show,
@@ -1493,36 +2183,6 @@ const struct seq_operations nfs_exports_op = {
.show = e_show,
};
-/**
- * nfsd_export_wq_init - allocate the export release workqueue
- *
- * Called once at module load. The workqueue runs deferred svc_export and
- * svc_expkey release work scheduled by queue_rcu_work() in the cache put
- * callbacks.
- *
- * Return values:
- * %0: workqueue allocated
- * %-ENOMEM: allocation failed
- */
-int nfsd_export_wq_init(void)
-{
- nfsd_export_wq = alloc_workqueue("nfsd_export", WQ_UNBOUND, 0);
- if (!nfsd_export_wq)
- return -ENOMEM;
- return 0;
-}
-
-/**
- * nfsd_export_wq_shutdown - drain and free the export release workqueue
- *
- * Called once at module unload. Per-namespace teardown in
- * nfsd_export_shutdown() has already drained all deferred work.
- */
-void nfsd_export_wq_shutdown(void)
-{
- destroy_workqueue(nfsd_export_wq);
-}
-
/*
* Initialize the exports module.
*/
@@ -1584,9 +2244,6 @@ nfsd_export_shutdown(struct net *net)
cache_unregister_net(nn->svc_expkey_cache, net);
cache_unregister_net(nn->svc_export_cache, net);
- /* Drain deferred export and expkey release work. */
- rcu_barrier();
- flush_workqueue(nfsd_export_wq);
cache_destroy_net(nn->svc_expkey_cache, net);
cache_destroy_net(nn->svc_export_cache, net);
svcauth_unix_purge(net);
diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h
index b05399374574..d2b09cd76145 100644
--- a/fs/nfsd/export.h
+++ b/fs/nfsd/export.h
@@ -7,7 +7,6 @@
#include <linux/sunrpc/cache.h>
#include <linux/percpu_counter.h>
-#include <linux/workqueue.h>
#include <uapi/linux/nfsd/export.h>
#include <linux/nfs4.h>
@@ -76,7 +75,7 @@ struct svc_export {
u32 ex_layout_types;
struct nfsd4_deviceid_map *ex_devid_map;
struct cache_detail *cd;
- struct rcu_work ex_rwork;
+ struct rcu_head ex_rcu;
unsigned long ex_xprtsec_modes;
struct export_stats *ex_stats;
};
@@ -93,7 +92,7 @@ struct svc_expkey {
u32 ek_fsid[6];
struct path ek_path;
- struct rcu_work ek_rwork;
+ struct rcu_head ek_rcu;
};
#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC))
@@ -111,8 +110,6 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
/*
* Function declarations
*/
-int nfsd_export_wq_init(void);
-void nfsd_export_wq_shutdown(void);
int nfsd_export_init(struct net *);
void nfsd_export_shutdown(struct net *);
void nfsd_export_flush(struct net *);
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 1e2b38ed1d35..24511c3208db 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -724,6 +724,52 @@ nfsd_file_close_inode_sync(struct inode *inode)
nfsd_file_dispose_list(&dispose);
}
+/**
+ * nfsd_file_close_export - close cached file handles for an export
+ * @net: net namespace in which to operate
+ * @path: export path whose cached files should be closed
+ *
+ * Close out GC-managed nfsd_file entries whose underlying file is on
+ * the same filesystem as, and a descendant of, @path. nfsd_file
+ * entries do not carry an export reference, so the check uses the
+ * file's dentry ancestry. False positives (closing a cached handle
+ * that did not originate from the target export) are harmless -- the
+ * handle is simply reopened on the next access.
+ *
+ * Called from the NFSD_CMD_UNLOCK_EXPORT handler before revoking
+ * NFSv4 state, to ensure that cached file handles do not hold the
+ * filesystem busy.
+ */
+void nfsd_file_close_export(struct net *net, const struct path *path)
+{
+ struct rhashtable_iter iter;
+ struct nfsd_file *nf;
+ LIST_HEAD(dispose);
+
+ rhltable_walk_enter(&nfsd_file_rhltable, &iter);
+ do {
+ rhashtable_walk_start(&iter);
+
+ nf = rhashtable_walk_next(&iter);
+ while (!IS_ERR_OR_NULL(nf)) {
+ if (nf->nf_net == net &&
+ test_bit(NFSD_FILE_GC, &nf->nf_flags) &&
+ nf->nf_file &&
+ file_inode(nf->nf_file)->i_sb ==
+ path->dentry->d_sb &&
+ is_subdir(nf->nf_file->f_path.dentry,
+ path->dentry))
+ nfsd_file_cond_queue(nf, &dispose);
+ nf = rhashtable_walk_next(&iter);
+ }
+
+ rhashtable_walk_stop(&iter);
+ } while (nf == ERR_PTR(-EAGAIN));
+ rhashtable_walk_exit(&iter);
+
+ nfsd_file_dispose_list(&dispose);
+}
+
static int
nfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg,
void *data)
diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h
index b383dbc5b921..683b6437cacc 100644
--- a/fs/nfsd/filecache.h
+++ b/fs/nfsd/filecache.h
@@ -70,6 +70,7 @@ struct net *nfsd_file_put_local(struct nfsd_file __rcu **nf);
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
struct file *nfsd_file_file(struct nfsd_file *nf);
void nfsd_file_close_inode_sync(struct inode *inode);
+void nfsd_file_close_export(struct net *net, const struct path *path);
void nfsd_file_net_dispose(struct nfsd_net *nn);
bool nfsd_file_is_cached(struct inode *inode);
__be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
index 81c943345d13..fbee3676d253 100644
--- a/fs/nfsd/netlink.c
+++ b/fs/nfsd/netlink.c
@@ -12,11 +12,51 @@
#include <uapi/linux/nfsd_netlink.h>
/* Common nested types */
+const struct nla_policy nfsd_auth_flavor_nl_policy[NFSD_A_AUTH_FLAVOR_FLAGS + 1] = {
+ [NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR] = { .type = NLA_U32, },
+ [NFSD_A_AUTH_FLAVOR_FLAGS] = NLA_POLICY_MASK(NLA_U32, 0x3ffff),
+};
+
+const struct nla_policy nfsd_expkey_nl_policy[NFSD_A_EXPKEY_PATH + 1] = {
+ [NFSD_A_EXPKEY_SEQNO] = { .type = NLA_U64, },
+ [NFSD_A_EXPKEY_CLIENT] = { .type = NLA_NUL_STRING, },
+ [NFSD_A_EXPKEY_FSIDTYPE] = { .type = NLA_U8, },
+ [NFSD_A_EXPKEY_FSID] = { .type = NLA_BINARY, },
+ [NFSD_A_EXPKEY_NEGATIVE] = { .type = NLA_FLAG, },
+ [NFSD_A_EXPKEY_EXPIRY] = { .type = NLA_U64, },
+ [NFSD_A_EXPKEY_PATH] = { .type = NLA_NUL_STRING, },
+};
+
+const struct nla_policy nfsd_fslocation_nl_policy[NFSD_A_FSLOCATION_PATH + 1] = {
+ [NFSD_A_FSLOCATION_HOST] = { .type = NLA_NUL_STRING, },
+ [NFSD_A_FSLOCATION_PATH] = { .type = NLA_NUL_STRING, },
+};
+
+const struct nla_policy nfsd_fslocations_nl_policy[NFSD_A_FSLOCATIONS_LOCATION + 1] = {
+ [NFSD_A_FSLOCATIONS_LOCATION] = NLA_POLICY_NESTED(nfsd_fslocation_nl_policy),
+};
+
const struct nla_policy nfsd_sock_nl_policy[NFSD_A_SOCK_TRANSPORT_NAME + 1] = {
[NFSD_A_SOCK_ADDR] = { .type = NLA_BINARY, },
[NFSD_A_SOCK_TRANSPORT_NAME] = { .type = NLA_NUL_STRING, },
};
+const struct nla_policy nfsd_svc_export_nl_policy[NFSD_A_SVC_EXPORT_FSID + 1] = {
+ [NFSD_A_SVC_EXPORT_SEQNO] = { .type = NLA_U64, },
+ [NFSD_A_SVC_EXPORT_CLIENT] = { .type = NLA_NUL_STRING, },
+ [NFSD_A_SVC_EXPORT_PATH] = { .type = NLA_NUL_STRING, },
+ [NFSD_A_SVC_EXPORT_NEGATIVE] = { .type = NLA_FLAG, },
+ [NFSD_A_SVC_EXPORT_EXPIRY] = { .type = NLA_U64, },
+ [NFSD_A_SVC_EXPORT_ANON_UID] = { .type = NLA_U32, },
+ [NFSD_A_SVC_EXPORT_ANON_GID] = { .type = NLA_U32, },
+ [NFSD_A_SVC_EXPORT_FSLOCATIONS] = NLA_POLICY_NESTED(nfsd_fslocations_nl_policy),
+ [NFSD_A_SVC_EXPORT_UUID] = { .type = NLA_BINARY, },
+ [NFSD_A_SVC_EXPORT_SECINFO] = NLA_POLICY_NESTED(nfsd_auth_flavor_nl_policy),
+ [NFSD_A_SVC_EXPORT_XPRTSEC] = NLA_POLICY_MASK(NLA_U32, 0x7),
+ [NFSD_A_SVC_EXPORT_FLAGS] = NLA_POLICY_MASK(NLA_U32, 0x3ffff),
+ [NFSD_A_SVC_EXPORT_FSID] = { .type = NLA_S32, },
+};
+
const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = {
[NFSD_A_VERSION_MAJOR] = { .type = NLA_U32, },
[NFSD_A_VERSION_MINOR] = { .type = NLA_U32, },
@@ -48,6 +88,36 @@ static const struct nla_policy nfsd_pool_mode_set_nl_policy[NFSD_A_POOL_MODE_MOD
[NFSD_A_POOL_MODE_MODE] = { .type = NLA_NUL_STRING, },
};
+/* NFSD_CMD_SVC_EXPORT_SET_REQS - do */
+static const struct nla_policy nfsd_svc_export_set_reqs_nl_policy[NFSD_A_SVC_EXPORT_REQS_REQUESTS + 1] = {
+ [NFSD_A_SVC_EXPORT_REQS_REQUESTS] = NLA_POLICY_NESTED(nfsd_svc_export_nl_policy),
+};
+
+/* NFSD_CMD_EXPKEY_SET_REQS - do */
+static const struct nla_policy nfsd_expkey_set_reqs_nl_policy[NFSD_A_EXPKEY_REQS_REQUESTS + 1] = {
+ [NFSD_A_EXPKEY_REQS_REQUESTS] = NLA_POLICY_NESTED(nfsd_expkey_nl_policy),
+};
+
+/* NFSD_CMD_CACHE_FLUSH - do */
+static const struct nla_policy nfsd_cache_flush_nl_policy[NFSD_A_CACHE_FLUSH_MASK + 1] = {
+ [NFSD_A_CACHE_FLUSH_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3),
+};
+
+/* NFSD_CMD_UNLOCK_IP - do */
+static const struct nla_policy nfsd_unlock_ip_nl_policy[NFSD_A_UNLOCK_IP_ADDRESS + 1] = {
+ [NFSD_A_UNLOCK_IP_ADDRESS] = NLA_POLICY_MIN_LEN(16),
+};
+
+/* NFSD_CMD_UNLOCK_FILESYSTEM - do */
+static const struct nla_policy nfsd_unlock_filesystem_nl_policy[NFSD_A_UNLOCK_FILESYSTEM_PATH + 1] = {
+ [NFSD_A_UNLOCK_FILESYSTEM_PATH] = { .type = NLA_NUL_STRING, },
+};
+
+/* NFSD_CMD_UNLOCK_EXPORT - do */
+static const struct nla_policy nfsd_unlock_export_nl_policy[NFSD_A_UNLOCK_EXPORT_PATH + 1] = {
+ [NFSD_A_UNLOCK_EXPORT_PATH] = { .type = NLA_NUL_STRING, },
+};
+
/* Ops table for nfsd */
static const struct genl_split_ops nfsd_nl_ops[] = {
{
@@ -103,6 +173,63 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
.doit = nfsd_nl_pool_mode_get_doit,
.flags = GENL_CMD_CAP_DO,
},
+ {
+ .cmd = NFSD_CMD_SVC_EXPORT_GET_REQS,
+ .dumpit = nfsd_nl_svc_export_get_reqs_dumpit,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = NFSD_CMD_SVC_EXPORT_SET_REQS,
+ .doit = nfsd_nl_svc_export_set_reqs_doit,
+ .policy = nfsd_svc_export_set_reqs_nl_policy,
+ .maxattr = NFSD_A_SVC_EXPORT_REQS_REQUESTS,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = NFSD_CMD_EXPKEY_GET_REQS,
+ .dumpit = nfsd_nl_expkey_get_reqs_dumpit,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = NFSD_CMD_EXPKEY_SET_REQS,
+ .doit = nfsd_nl_expkey_set_reqs_doit,
+ .policy = nfsd_expkey_set_reqs_nl_policy,
+ .maxattr = NFSD_A_EXPKEY_REQS_REQUESTS,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = NFSD_CMD_CACHE_FLUSH,
+ .doit = nfsd_nl_cache_flush_doit,
+ .policy = nfsd_cache_flush_nl_policy,
+ .maxattr = NFSD_A_CACHE_FLUSH_MASK,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = NFSD_CMD_UNLOCK_IP,
+ .doit = nfsd_nl_unlock_ip_doit,
+ .policy = nfsd_unlock_ip_nl_policy,
+ .maxattr = NFSD_A_UNLOCK_IP_ADDRESS,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = NFSD_CMD_UNLOCK_FILESYSTEM,
+ .doit = nfsd_nl_unlock_filesystem_doit,
+ .policy = nfsd_unlock_filesystem_nl_policy,
+ .maxattr = NFSD_A_UNLOCK_FILESYSTEM_PATH,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = NFSD_CMD_UNLOCK_EXPORT,
+ .doit = nfsd_nl_unlock_export_doit,
+ .policy = nfsd_unlock_export_nl_policy,
+ .maxattr = NFSD_A_UNLOCK_EXPORT_PATH,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+};
+
+static const struct genl_multicast_group nfsd_nl_mcgrps[] = {
+ [NFSD_NLGRP_NONE] = { "none", },
+ [NFSD_NLGRP_EXPORTD] = { "exportd", },
};
struct genl_family nfsd_nl_family __ro_after_init = {
@@ -113,4 +240,6 @@ struct genl_family nfsd_nl_family __ro_after_init = {
.module = THIS_MODULE,
.split_ops = nfsd_nl_ops,
.n_split_ops = ARRAY_SIZE(nfsd_nl_ops),
+ .mcgrps = nfsd_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(nfsd_nl_mcgrps),
};
diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h
index 478117ff6b8c..af41aa0d4a65 100644
--- a/fs/nfsd/netlink.h
+++ b/fs/nfsd/netlink.h
@@ -13,7 +13,12 @@
#include <uapi/linux/nfsd_netlink.h>
/* Common nested types */
+extern const struct nla_policy nfsd_auth_flavor_nl_policy[NFSD_A_AUTH_FLAVOR_FLAGS + 1];
+extern const struct nla_policy nfsd_expkey_nl_policy[NFSD_A_EXPKEY_PATH + 1];
+extern const struct nla_policy nfsd_fslocation_nl_policy[NFSD_A_FSLOCATION_PATH + 1];
+extern const struct nla_policy nfsd_fslocations_nl_policy[NFSD_A_FSLOCATIONS_LOCATION + 1];
extern const struct nla_policy nfsd_sock_nl_policy[NFSD_A_SOCK_TRANSPORT_NAME + 1];
+extern const struct nla_policy nfsd_svc_export_nl_policy[NFSD_A_SVC_EXPORT_FSID + 1];
extern const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1];
int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
@@ -26,6 +31,22 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_pool_mode_set_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_pool_mode_get_doit(struct sk_buff *skb, struct genl_info *info);
+int nfsd_nl_svc_export_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb);
+int nfsd_nl_svc_export_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info);
+int nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb);
+int nfsd_nl_expkey_set_reqs_doit(struct sk_buff *skb, struct genl_info *info);
+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);
+
+enum {
+ NFSD_NLGRP_NONE,
+ NFSD_NLGRP_EXPORTD,
+};
extern struct genl_family nfsd_nl_family;
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index 0ac538c76180..76305b86c1a9 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -131,10 +131,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
resp->status = fh_getattr(fh, &resp->stat);
out:
- /* argp->acl_{access,default} may have been allocated in
- nfssvc_decode_setaclargs. */
- posix_acl_release(argp->acl_access);
- posix_acl_release(argp->acl_default);
+ /* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */
return rpc_success;
out_drop_lock:
@@ -310,6 +307,16 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
fh_put(&resp->fh);
}
+static void nfsaclsvc_release_setacl(struct svc_rqst *rqstp)
+{
+ struct nfsd3_setaclargs *argp = rqstp->rq_argp;
+ struct nfsd_attrstat *resp = rqstp->rq_resp;
+
+ fh_put(&resp->fh);
+ posix_acl_release(argp->acl_access);
+ posix_acl_release(argp->acl_default);
+}
+
#define ST 1 /* status*/
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
@@ -343,7 +350,7 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
.pc_func = nfsacld_proc_setacl,
.pc_decode = nfsaclsvc_decode_setaclargs,
.pc_encode = nfssvc_encode_attrstatres,
- .pc_release = nfssvc_release_attrstat,
+ .pc_release = nfsaclsvc_release_setacl,
.pc_argsize = sizeof(struct nfsd3_setaclargs),
.pc_argzero = sizeof(struct nfsd3_setaclargs),
.pc_ressize = sizeof(struct nfsd_attrstat),
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index 7b5433bd3019..e87731380be8 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -118,10 +118,7 @@ out_drop_lock:
out_errno:
resp->status = nfserrno(error);
out:
- /* argp->acl_{access,default} may have been allocated in
- nfs3svc_decode_setaclargs. */
- posix_acl_release(argp->acl_access);
- posix_acl_release(argp->acl_default);
+ /* argp->acl_{access,default} are released in nfs3svc_release_setacl. */
return rpc_success;
}
@@ -223,6 +220,16 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp)
posix_acl_release(resp->acl_default);
}
+static void nfs3svc_release_setacl(struct svc_rqst *rqstp)
+{
+ struct nfsd3_setaclargs *argp = rqstp->rq_argp;
+ struct nfsd3_attrstat *resp = rqstp->rq_resp;
+
+ fh_put(&resp->fh);
+ posix_acl_release(argp->acl_access);
+ posix_acl_release(argp->acl_default);
+}
+
#define ST 1 /* status*/
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
@@ -256,7 +263,7 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
.pc_func = nfsd3_proc_setacl,
.pc_decode = nfs3svc_decode_setaclargs,
.pc_encode = nfs3svc_encode_setaclres,
- .pc_release = nfs3svc_release_fhandle,
+ .pc_release = nfs3svc_release_setacl,
.pc_argsize = sizeof(struct nfsd3_setaclargs),
.pc_argzero = sizeof(struct nfsd3_setaclargs),
.pc_ressize = sizeof(struct nfsd3_attrstat),
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c
index ba06d3d3e6dd..71ba61b5d0a3 100644
--- a/fs/nfsd/nfs4idmap.c
+++ b/fs/nfsd/nfs4idmap.c
@@ -126,7 +126,7 @@ idtoname_hash(struct ent *ent)
static int
idtoname_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall_timeout(cd, h);
+ return sunrpc_cache_upcall_warn(cd, h);
}
static void
@@ -306,7 +306,7 @@ nametoid_hash(struct ent *ent)
static int
nametoid_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall_timeout(cd, h);
+ return sunrpc_cache_upcall_warn(cd, h);
}
static void
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index c3543d456702..f34320e4c2f4 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -234,6 +234,8 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
get_nfs4_file(fp);
stp->sc_file = fp;
+ if (parent->sc_export)
+ stp->sc_export = exp_get(parent->sc_export);
ls = layoutstateid(stp);
INIT_LIST_HEAD(&ls->ls_perclnt);
@@ -251,10 +253,12 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
ls->ls_file = find_any_file(fp);
BUG_ON(!ls->ls_file);
+ ls->ls_fenced = false;
+ ls->ls_fence_delay = 0;
+ INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker);
+
if (nfsd4_layout_setlease(ls)) {
- nfsd_file_put(ls->ls_file);
- put_nfs4_file(fp);
- kmem_cache_free(nfs4_layout_stateid_cache, ls);
+ nfs4_put_stid(stp);
return NULL;
}
@@ -267,10 +271,6 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
list_add(&ls->ls_perfile, &fp->fi_lo_states);
spin_unlock(&fp->fi_lock);
- ls->ls_fenced = false;
- ls->ls_fence_delay = 0;
- INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker);
-
trace_nfsd_layoutstate_alloc(&ls->ls_stid.sc_stateid);
return ls;
}
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index ab39ec885440..8561540ab2db 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -253,7 +253,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
.na_iattr = iap,
.na_seclabel = &open->op_label,
};
- struct dentry *parent, *child;
+ struct dentry *parent, *child = ERR_PTR(-EINVAL);
__u32 v_mtime, v_atime;
struct inode *inode;
__be32 status;
@@ -277,10 +277,14 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (open->op_acl) {
if (open->op_dpacl || open->op_pacl) {
status = nfserr_inval;
- goto out_write;
+ goto out;
+ }
+ if (is_create_with_attrs(open)) {
+ status = nfsd4_acl_to_attr(NF4REG, open->op_acl,
+ &attrs);
+ if (status)
+ goto out;
}
- if (is_create_with_attrs(open))
- nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
} else if (is_create_with_attrs(open)) {
/* The dpacl and pacl will get released by nfsd_attrs_free(). */
attrs.na_dpacl = open->op_dpacl;
@@ -293,7 +297,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
&QSTR_LEN(open->op_fname, open->op_fnamelen));
if (IS_ERR(child)) {
status = nfserrno(PTR_ERR(child));
- goto out_write;
+ goto out;
}
if (d_really_is_negative(child)) {
@@ -407,7 +411,6 @@ set_attr:
out:
end_creating(child);
nfsd_attrs_free(&attrs);
-out_write:
fh_drop_write(fhp);
return status;
}
@@ -837,7 +840,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out_aftermask;
if (create->cr_acl) {
- if (create->cr_dpacl || create->cr_pacl) {
+ if (attrs.na_dpacl || attrs.na_pacl) {
status = nfserr_inval;
goto out_aftermask;
}
@@ -1470,7 +1473,7 @@ void nfsd4_async_copy_reaper(struct nfsd_net *nn)
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)) {
- if (--copy->cp_ttl) {
+ if (!--copy->cp_ttl) {
list_del_init(&copy->copies);
list_add(&copy->copies, &reaplist);
}
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index b338473d6e52..6ea25a52d2f4 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -718,7 +718,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
return PTR_ERR(namecopy);
name.data = namecopy;
name.len = namelen;
- get_user(princhashlen, &ci->cc_princhash.cp_len);
+ if (get_user(princhashlen, &ci->cc_princhash.cp_len))
+ return -EFAULT;
if (princhashlen > 0) {
princhashcopy = memdup_user(
&ci->cc_princhash.cp_data,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6837b63d9864..a42f34842d77 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1168,6 +1168,7 @@ out_dec:
void
nfs4_put_stid(struct nfs4_stid *s)
{
+ struct svc_export *exp = s->sc_export;
struct nfs4_file *fp = s->sc_file;
struct nfs4_client *clp = s->sc_client;
@@ -1183,6 +1184,8 @@ nfs4_put_stid(struct nfs4_stid *s)
nfs4_free_cpntf_statelist(clp->net, s);
spin_unlock(&clp->cl_lock);
s->sc_free(s);
+ if (exp)
+ exp_put(exp);
if (fp)
put_nfs4_file(fp);
}
@@ -1376,7 +1379,8 @@ static void destroy_delegation(struct nfs4_delegation *dp)
* stateid or it's called from a laundromat thread (nfsd4_landromat()) that
* determined that this specific state has expired and needs to be revoked
* (both mark state with the appropriate stid sc_status mode). It is also
- * assumed that a reference was taken on the @dp state.
+ * assumed that a reference was taken on the @dp state. This function
+ * consumes that reference.
*
* If this function finds that the @dp state is SC_STATUS_FREED it means
* that a FREE_STATEID operation for this stateid has been processed and
@@ -1745,21 +1749,119 @@ static struct nfs4_stid *find_one_sb_stid(struct nfs4_client *clp,
struct super_block *sb,
unsigned int sc_types)
{
- unsigned long id, tmp;
+ unsigned long id = 0;
struct nfs4_stid *stid;
spin_lock(&clp->cl_lock);
- idr_for_each_entry_ul(&clp->cl_stateids, stid, tmp, id)
+ while ((stid = idr_get_next_ul(&clp->cl_stateids, &id)) != NULL) {
if ((stid->sc_type & sc_types) &&
stid->sc_status == 0 &&
stid->sc_file->fi_inode->i_sb == sb) {
refcount_inc(&stid->sc_count);
break;
}
+ id++;
+ }
spin_unlock(&clp->cl_lock);
return stid;
}
+/*
+ * Release the export reference an admin-revoked stateid holds,
+ * so the svc_export (and its vfsmount) is not pinned until the
+ * client issues FREE_STATEID. sc_export is no longer consulted
+ * once SC_STATUS_ADMIN_REVOKED is set.
+ */
+static void drop_stid_export(struct nfs4_client *clp,
+ struct nfs4_stid *stid)
+{
+ struct svc_export *exp;
+
+ spin_lock(&clp->cl_lock);
+ exp = stid->sc_export;
+ stid->sc_export = NULL;
+ spin_unlock(&clp->cl_lock);
+ if (exp)
+ exp_put(exp);
+}
+
+static void revoke_ol_stid(struct nfs4_client *clp,
+ struct nfs4_ol_stateid *stp)
+{
+ struct nfs4_stid *stid = &stp->st_stid;
+
+ lockdep_assert_held(&stp->st_mutex);
+ spin_lock(&clp->cl_lock);
+ if (stid->sc_status == 0) {
+ stid->sc_status |= SC_STATUS_ADMIN_REVOKED;
+ atomic_inc(&clp->cl_admin_revoked);
+ spin_unlock(&clp->cl_lock);
+ if (stid->sc_type == SC_TYPE_LOCK) {
+ struct nfs4_lockowner *lo =
+ lockowner(stp->st_stateowner);
+ struct nfsd_file *nf;
+
+ nf = find_any_file(stp->st_stid.sc_file);
+ if (nf) {
+ get_file(nf->nf_file);
+ filp_close(nf->nf_file, (fl_owner_t)lo);
+ nfsd_file_put(nf);
+ }
+ }
+ release_all_access(stp);
+ drop_stid_export(clp, stid);
+ } else
+ spin_unlock(&clp->cl_lock);
+}
+
+static void revoke_one_stid(struct nfsd_net *nn, struct nfs4_client *clp,
+ struct nfs4_stid *stid)
+{
+ struct nfs4_ol_stateid *stp;
+ struct nfs4_delegation *dp;
+
+ switch (stid->sc_type) {
+ case SC_TYPE_OPEN:
+ stp = openlockstateid(stid);
+ mutex_lock_nested(&stp->st_mutex, OPEN_STATEID_MUTEX);
+ revoke_ol_stid(clp, stp);
+ mutex_unlock(&stp->st_mutex);
+ break;
+ case SC_TYPE_LOCK:
+ stp = openlockstateid(stid);
+ mutex_lock_nested(&stp->st_mutex, LOCK_STATEID_MUTEX);
+ revoke_ol_stid(clp, stp);
+ mutex_unlock(&stp->st_mutex);
+ break;
+ case SC_TYPE_DELEG:
+ /*
+ * Extra reference guards against concurrent FREE_STATEID.
+ */
+ refcount_inc(&stid->sc_count);
+ dp = delegstateid(stid);
+ spin_lock(&nn->deleg_lock);
+ if (!unhash_delegation_locked(dp, SC_STATUS_ADMIN_REVOKED))
+ dp = NULL;
+ spin_unlock(&nn->deleg_lock);
+ if (dp) {
+ revoke_delegation(dp);
+ drop_stid_export(clp, stid);
+ } else
+ nfs4_put_stid(stid);
+ break;
+ case SC_TYPE_LAYOUT:
+ spin_lock(&clp->cl_lock);
+ if (stid->sc_status == 0) {
+ stid->sc_status |= SC_STATUS_ADMIN_REVOKED;
+ atomic_inc(&clp->cl_admin_revoked);
+ }
+ spin_unlock(&clp->cl_lock);
+ nfsd4_close_layout(layoutstateid(stid));
+ drop_stid_export(clp, stid);
+ break;
+ }
+}
+
/**
* nfsd4_revoke_states - revoke all nfsv4 states associated with given filesystem
* @nn: used to identify instance of nfsd (there is one per net namespace)
@@ -1790,77 +1892,8 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
struct nfs4_stid *stid = find_one_sb_stid(clp, sb,
sc_types);
if (stid) {
- struct nfs4_ol_stateid *stp;
- struct nfs4_delegation *dp;
- struct nfs4_layout_stateid *ls;
-
spin_unlock(&nn->client_lock);
- switch (stid->sc_type) {
- case SC_TYPE_OPEN:
- stp = openlockstateid(stid);
- mutex_lock_nested(&stp->st_mutex,
- OPEN_STATEID_MUTEX);
-
- spin_lock(&clp->cl_lock);
- if (stid->sc_status == 0) {
- stid->sc_status |=
- SC_STATUS_ADMIN_REVOKED;
- atomic_inc(&clp->cl_admin_revoked);
- spin_unlock(&clp->cl_lock);
- release_all_access(stp);
- } else
- spin_unlock(&clp->cl_lock);
- mutex_unlock(&stp->st_mutex);
- break;
- case SC_TYPE_LOCK:
- stp = openlockstateid(stid);
- mutex_lock_nested(&stp->st_mutex,
- LOCK_STATEID_MUTEX);
- spin_lock(&clp->cl_lock);
- if (stid->sc_status == 0) {
- struct nfs4_lockowner *lo =
- lockowner(stp->st_stateowner);
- struct nfsd_file *nf;
-
- stid->sc_status |=
- SC_STATUS_ADMIN_REVOKED;
- atomic_inc(&clp->cl_admin_revoked);
- spin_unlock(&clp->cl_lock);
- nf = find_any_file(stp->st_stid.sc_file);
- if (nf) {
- get_file(nf->nf_file);
- filp_close(nf->nf_file,
- (fl_owner_t)lo);
- nfsd_file_put(nf);
- }
- release_all_access(stp);
- } else
- spin_unlock(&clp->cl_lock);
- mutex_unlock(&stp->st_mutex);
- break;
- case SC_TYPE_DELEG:
- refcount_inc(&stid->sc_count);
- dp = delegstateid(stid);
- spin_lock(&nn->deleg_lock);
- if (!unhash_delegation_locked(
- dp, SC_STATUS_ADMIN_REVOKED))
- dp = NULL;
- spin_unlock(&nn->deleg_lock);
- if (dp)
- revoke_delegation(dp);
- break;
- case SC_TYPE_LAYOUT:
- ls = layoutstateid(stid);
- spin_lock(&clp->cl_lock);
- if (stid->sc_status == 0) {
- stid->sc_status |=
- SC_STATUS_ADMIN_REVOKED;
- atomic_inc(&clp->cl_admin_revoked);
- }
- spin_unlock(&clp->cl_lock);
- nfsd4_close_layout(ls);
- break;
- }
+ revoke_one_stid(nn, clp, stid);
nfs4_put_stid(stid);
spin_lock(&nn->client_lock);
if (clp->cl_minorversion == 0)
@@ -1878,6 +1911,73 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
spin_unlock(&nn->client_lock);
}
+static struct nfs4_stid *find_one_export_stid(struct nfs4_client *clp,
+ const struct path *path,
+ unsigned int sc_types)
+{
+ unsigned long id = 0;
+ struct nfs4_stid *stid;
+
+ spin_lock(&clp->cl_lock);
+ while ((stid = idr_get_next_ul(&clp->cl_stateids, &id)) != NULL) {
+ if ((stid->sc_type & sc_types) &&
+ stid->sc_status == 0 &&
+ stid->sc_export &&
+ path_equal(&stid->sc_export->ex_path, path)) {
+ refcount_inc(&stid->sc_count);
+ break;
+ }
+ id++;
+ }
+ spin_unlock(&clp->cl_lock);
+ return stid;
+}
+
+/**
+ * nfsd4_revoke_export_states - revoke nfsv4 states acquired through an export
+ * @nn: used to identify instance of nfsd (there is one per net namespace)
+ * @path: export path whose states should be revoked
+ *
+ * All nfs4 states (open, lock, delegation, layout) acquired through any
+ * export matching @path are revoked, regardless of which client holds
+ * them. Matching is by path identity (dentry + vfsmount), so multiple
+ * svc_export objects for the same path -- one per auth_domain -- are
+ * handled correctly.
+ *
+ * Userspace (exportfs -u) sends this after removing the last client
+ * for a path, enabling the underlying filesystem to be unmounted.
+ */
+void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path)
+{
+ unsigned int idhashval;
+ unsigned int sc_types;
+
+ sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT;
+
+ spin_lock(&nn->client_lock);
+ for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
+ struct list_head *head = &nn->conf_id_hashtbl[idhashval];
+ struct nfs4_client *clp;
+ retry:
+ list_for_each_entry(clp, head, cl_idhash) {
+ struct nfs4_stid *stid = find_one_export_stid(
+ clp, path,
+ sc_types);
+ if (stid) {
+ spin_unlock(&nn->client_lock);
+ revoke_one_stid(nn, clp, stid);
+ nfs4_put_stid(stid);
+ spin_lock(&nn->client_lock);
+ if (clp->cl_minorversion == 0)
+ nn->nfs40_last_revoke =
+ ktime_get_boottime_seconds();
+ goto retry;
+ }
+ }
+ }
+ spin_unlock(&nn->client_lock);
+}
+
static inline int
hash_sessionid(struct nfs4_sessionid *sessionid)
{
@@ -5049,6 +5149,7 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s)
{
struct nfs4_client *cl = s->sc_client;
LIST_HEAD(reaplist);
+ struct nfs4_layout_stateid *ls;
struct nfs4_ol_stateid *stp;
struct nfs4_delegation *dp;
bool unhashed;
@@ -5074,6 +5175,12 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s)
spin_unlock(&cl->cl_lock);
nfs4_put_stid(s);
break;
+ case SC_TYPE_LAYOUT:
+ ls = layoutstateid(s);
+ list_del_init(&ls->ls_perclnt);
+ spin_unlock(&cl->cl_lock);
+ nfs4_put_stid(s);
+ break;
default:
spin_unlock(&cl->cl_lock);
}
@@ -5169,6 +5276,7 @@ retry:
/* Replace unconfirmed owners without checking for replay. */
release_openowner(oo);
oo = NULL;
+ goto retry;
}
if (oo) {
if (new)
@@ -6157,6 +6265,8 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
dp = alloc_init_deleg(clp, fp, odstate, dl_type);
if (!dp)
goto out_delegees;
+ if (stp->st_stid.sc_export)
+ dp->dl_stid.sc_export = exp_get(stp->st_stid.sc_export);
fl = nfs4_alloc_init_lease(dp);
if (!fl)
@@ -6490,8 +6600,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
goto out;
}
- if (!open->op_stp)
+ if (!open->op_stp) {
new_stp = true;
+ stp->st_stid.sc_export =
+ exp_get(current_fh->fh_export);
+ }
}
/*
@@ -8187,6 +8300,9 @@ retry:
stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner);
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
+ if (open_stp->st_stid.sc_export)
+ stp->st_stid.sc_export =
+ exp_get(open_stp->st_stid.sc_export);
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp;
@@ -9517,6 +9633,9 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ);
if (!dp)
goto out_delegees;
+ if (cstate->current_fh.fh_export)
+ dp->dl_stid.sc_export =
+ exp_get(cstate->current_fh.fh_export);
fl = nfs4_alloc_init_lease(dp);
if (!fl)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 20355dc3f1d1..e17488a911f7 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2008,10 +2008,11 @@ static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
union nfsd4_op_u *u)
{
struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name;
+
+ sin->sin_exp = NULL;
if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0)
return nfserr_bad_xdr;
- sin->sin_exp = NULL;
return nfs_ok;
}
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 04e3954d54bd..d0486f4a47ba 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -10,6 +10,7 @@
#include <linux/ctype.h>
#include <linux/fs_context.h>
+#include <linux/sunrpc/cache.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/lockd/bind.h>
#include <linux/sunrpc/addr.h>
@@ -246,7 +247,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
if (rpc_pton(net, fo_path, size, sap, salen) == 0)
return -EINVAL;
- trace_nfsd_ctl_unlock_ip(net, buf);
+ trace_nfsd_ctl_unlock_ip(net, sap, svc_addr_len(sap));
return nlmsvc_unlock_all_by_ip(sap);
}
@@ -1412,6 +1413,21 @@ static int create_proc_exports_entry(void)
unsigned int nfsd_net_id;
+struct nfsd_genl_rqstp {
+ struct sockaddr rq_daddr;
+ struct sockaddr rq_saddr;
+ unsigned long rq_flags;
+ ktime_t rq_stime;
+ __be32 rq_xid;
+ u32 rq_vers;
+ u32 rq_prog;
+ u32 rq_proc;
+
+ /* NFSv4 compound */
+ u32 rq_opcnt;
+ u32 rq_opnum[16];
+};
+
static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
struct netlink_callback *cb,
struct nfsd_genl_rqstp *genl_rqstp)
@@ -2198,6 +2214,193 @@ err_free_msg:
}
/**
+ * nfsd_nl_cache_flush_doit - flush nfsd caches via netlink
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Flush the svc_export and/or expkey caches. If NFSD_A_CACHE_FLUSH_MASK
+ * is provided, only flush the caches indicated by the bitmask (bit 0 =
+ * svc_export, bit 1 = expkey). If omitted, flush both.
+ *
+ * Return 0 on success or a negative errno.
+ */
+int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ u32 mask = ~0U;
+
+ if (info->attrs[NFSD_A_CACHE_FLUSH_MASK])
+ mask = nla_get_u32(info->attrs[NFSD_A_CACHE_FLUSH_MASK]);
+
+ mutex_lock(&nfsd_mutex);
+
+ if ((mask & NFSD_CACHE_TYPE_SVC_EXPORT) &&
+ nn->svc_export_cache)
+ cache_purge(nn->svc_export_cache);
+
+ if ((mask & NFSD_CACHE_TYPE_EXPKEY) &&
+ nn->svc_expkey_cache)
+ cache_purge(nn->svc_expkey_cache);
+
+ mutex_unlock(&nfsd_mutex);
+
+ return 0;
+}
+
+int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_type)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *msg;
+
+ if (!genl_has_listeners(&nfsd_nl_family, cd->net, NFSD_NLGRP_EXPORTD))
+ return -ENOLINK;
+
+ msg = genlmsg_new(nla_total_size(sizeof(u32)), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfsd_nl_family, 0, NFSD_CMD_CACHE_NOTIFY);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(msg, NFSD_A_CACHE_NOTIFY_CACHE_TYPE, cache_type)) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_multicast_netns(&nfsd_nl_family, cd->net, msg, 0,
+ NFSD_NLGRP_EXPORTD, GFP_KERNEL);
+}
+
+/**
+ * nfsd_nl_unlock_ip_doit - release NLM locks held by an IP address
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Return: 0 on success or a negative errno.
+ */
+int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sockaddr *sap;
+
+ if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_IP_ADDRESS))
+ return -EINVAL;
+ sap = nla_data(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]);
+ switch (sap->sa_family) {
+ case AF_INET:
+ if (nla_len(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]) <
+ sizeof(struct sockaddr_in))
+ return -EINVAL;
+ break;
+ case AF_INET6:
+ if (nla_len(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]) <
+ sizeof(struct sockaddr_in6))
+ return -EINVAL;
+ break;
+ default:
+ return -EAFNOSUPPORT;
+ }
+ /*
+ * nlmsvc_unlock_all_by_ip() releases matching locks
+ * across all network namespaces because lockd operates
+ * a single global instance.
+ */
+ trace_nfsd_ctl_unlock_ip(genl_info_net(info), sap,
+ svc_addr_len(sap));
+ return nlmsvc_unlock_all_by_ip(sap);
+}
+
+/**
+ * nfsd_nl_unlock_filesystem_doit - revoke NFS state under a filesystem path
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Return: 0 on success or a negative errno.
+ */
+int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct path path;
+ int error;
+
+ if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_FILESYSTEM_PATH))
+ return -EINVAL;
+
+ trace_nfsd_ctl_unlock_fs(net,
+ nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH]));
+ error = kern_path(
+ nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH]),
+ 0, &path);
+ if (error)
+ return error;
+
+ nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb);
+ error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
+
+ mutex_lock(&nfsd_mutex);
+ if (nn->nfsd_serv)
+ nfsd4_revoke_states(nn, path.dentry->d_sb);
+ else
+ error = -EINVAL;
+ mutex_unlock(&nfsd_mutex);
+
+ path_put(&path);
+ return error;
+}
+
+/**
+ * nfsd_nl_unlock_export_doit - revoke NFSv4 state for an export path
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Revokes all NFSv4 state (opens, locks, delegations, layouts) acquired
+ * through any export of the given path, regardless of which client holds
+ * the state. Userspace (exportfs -u) sends this after removing the last
+ * client for a path so the underlying filesystem can be unmounted.
+ *
+ * Unlike NFSD_CMD_UNLOCK_FILESYSTEM, which operates at superblock
+ * granularity, this command revokes only the state associated with
+ * exports of a specific path.
+ *
+ * Return: 0 on success or a negative errno.
+ */
+int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct path path;
+ int error;
+
+ if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_EXPORT_PATH))
+ return -EINVAL;
+
+ trace_nfsd_ctl_unlock_export(net,
+ nla_data(info->attrs[NFSD_A_UNLOCK_EXPORT_PATH]));
+ error = kern_path(
+ nla_data(info->attrs[NFSD_A_UNLOCK_EXPORT_PATH]),
+ 0, &path);
+ if (error)
+ return error;
+
+ mutex_lock(&nfsd_mutex);
+ if (nn->nfsd_serv) {
+ nfsd_file_close_export(net, &path);
+ nfsd4_revoke_export_states(nn, &path);
+ } else
+ error = -EINVAL;
+ mutex_unlock(&nfsd_mutex);
+
+ path_put(&path);
+ return error;
+}
+
+/**
* nfsd_net_init - Prepare the nfsd_net portion of a new net namespace
* @net: a freshly-created network namespace
*
@@ -2320,12 +2523,9 @@ static int __init init_nfsd(void)
if (retval)
goto out_free_pnfs;
nfsd_lockd_init(); /* lockd->nfsd callbacks */
- retval = nfsd_export_wq_init();
- if (retval)
- goto out_free_lockd;
retval = register_pernet_subsys(&nfsd_net_ops);
if (retval < 0)
- goto out_free_export_wq;
+ goto out_free_lockd;
retval = register_cld_notifier();
if (retval)
goto out_free_subsys;
@@ -2354,8 +2554,6 @@ out_free_cld:
unregister_cld_notifier();
out_free_subsys:
unregister_pernet_subsys(&nfsd_net_ops);
-out_free_export_wq:
- nfsd_export_wq_shutdown();
out_free_lockd:
nfsd_lockd_shutdown();
nfsd_drc_slab_free();
@@ -2376,7 +2574,6 @@ static void __exit exit_nfsd(void)
nfsd4_destroy_laundry_wq();
unregister_cld_notifier();
unregister_pernet_subsys(&nfsd_net_ops);
- nfsd_export_wq_shutdown();
nfsd_drc_slab_free();
nfsd_lockd_shutdown();
nfsd4_free_slabs();
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 7c009f07c90b..11bce03b9031 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -45,11 +45,10 @@ bool nfsd_support_version(int vers);
/*
* Default and maximum payload size (NFS READ or WRITE), in bytes.
- * The default is historical, and the maximum is an implementation
- * limit.
+ * The maximum is an implementation limit.
*/
enum {
- NFSSVC_DEFBLKSIZE = 1 * 1024 * 1024,
+ NFSSVC_DEFBLKSIZE = 4 * 1024 * 1024,
NFSSVC_MAXBLKSIZE = RPCSVC_MAXPAYLOAD,
};
@@ -60,21 +59,6 @@ struct readdir_cd {
/* Maximum number of operations per session compound */
#define NFSD_MAX_OPS_PER_COMPOUND 200
-struct nfsd_genl_rqstp {
- struct sockaddr rq_daddr;
- struct sockaddr rq_saddr;
- unsigned long rq_flags;
- ktime_t rq_stime;
- __be32 rq_xid;
- u32 rq_vers;
- u32 rq_prog;
- u32 rq_proc;
-
- /* NFSv4 compound */
- u32 rq_opcnt;
- u32 rq_opnum[16];
-};
-
extern struct svc_program nfsd_programs[];
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
extern struct mutex nfsd_mutex;
@@ -123,7 +107,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
const struct tree_descr *,
struct dentry **fdentries);
void nfsd_client_rmdir(struct dentry *dentry);
-
+int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_type);
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
#ifdef CONFIG_NFSD_V2_ACL
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index c5ccea64c281..dec83e92650d 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -145,6 +145,7 @@ struct nfs4_stid {
spinlock_t sc_lock;
struct nfs4_client *sc_client;
struct nfs4_file *sc_file;
+ struct svc_export *sc_export;
void (*sc_free)(struct nfs4_stid *);
};
@@ -862,6 +863,7 @@ struct nfsd_file *find_any_file(struct nfs4_file *f);
#ifdef CONFIG_NFSD_V4
void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb);
+void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path);
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb);
int nfsd_net_cb_init(struct nfsd_net *nn);
void nfsd_net_cb_shutdown(struct nfsd_net *nn);
@@ -869,6 +871,10 @@ void nfsd_net_cb_shutdown(struct nfsd_net *nn);
static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
{
}
+static inline void nfsd4_revoke_export_states(struct nfsd_net *nn,
+ const struct path *path)
+{
+}
static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
{
}
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index b631a472222b..1c5a1e50f946 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -1985,19 +1985,20 @@ TRACE_EVENT(nfsd_cb_recall_any_done,
TRACE_EVENT(nfsd_ctl_unlock_ip,
TP_PROTO(
const struct net *net,
- const char *address
+ const struct sockaddr *addr,
+ const unsigned int addrlen
),
- TP_ARGS(net, address),
+ TP_ARGS(net, addr, addrlen),
TP_STRUCT__entry(
__field(unsigned int, netns_ino)
- __string(address, address)
+ __sockaddr(addr, addrlen)
),
TP_fast_assign(
__entry->netns_ino = net->ns.inum;
- __assign_str(address);
+ __assign_sockaddr(addr, addr, addrlen);
),
- TP_printk("address=%s",
- __get_str(address)
+ TP_printk("addr=%pISpc",
+ __get_sockaddr(addr)
)
);
@@ -2020,6 +2021,25 @@ TRACE_EVENT(nfsd_ctl_unlock_fs,
)
);
+TRACE_EVENT(nfsd_ctl_unlock_export,
+ TP_PROTO(
+ const struct net *net,
+ const char *path
+ ),
+ TP_ARGS(net, path),
+ TP_STRUCT__entry(
+ __field(unsigned int, netns_ino)
+ __string(path, path)
+ ),
+ TP_fast_assign(
+ __entry->netns_ino = net->ns.inum;
+ __assign_str(path);
+ ),
+ TP_printk("path=%s",
+ __get_str(path)
+ )
+);
+
TRACE_EVENT(nfsd_ctl_filehandle,
TP_PROTO(
const struct net *net,
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 653cd6fea202..1e89c7ff9493 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1440,7 +1440,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned long exp_op_flags = 0;
unsigned int pflags = current->flags;
bool restore_flags = false;
- unsigned int nvecs;
+ int nvecs;
trace_nfsd_write_opened(rqstp, fhp, offset, *cnt);
@@ -1480,6 +1480,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
nvecs = xdr_buf_to_bvec(rqstp->rq_bvec, rqstp->rq_maxpages, payload);
+ if (nvecs < 0) {
+ host_err = nvecs;
+ goto out_nfserr;
+ }
since = READ_ONCE(file->f_wb_err);
if (verf)
@@ -1509,8 +1513,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
nfsd_stats_io_write_add(nn, exp, *cnt);
fsnotify_modify(file);
host_err = filemap_check_wb_err(file->f_mapping, since);
- if (host_err < 0)
+ if (host_err < 0) {
+ commit_reset_write_verifier(nn, rqstp, host_err);
goto out_nfserr;
+ }
if (stable && fhp->fh_use_wgather) {
host_err = wait_for_concurrent_writes(file);
@@ -1690,6 +1696,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
nfsd_copy_write_verifier(verf, nn);
err2 = filemap_check_wb_err(nf->nf_file->f_mapping,
since);
+ if (err2 < 0)
+ commit_reset_write_verifier(nn, rqstp, err2);
err = nfserrno(err2);
break;
case -EINVAL:
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index b1e595c2615b..2735c332ddb7 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -80,6 +80,9 @@ struct cache_detail {
int (*cache_upcall)(struct cache_detail *,
struct cache_head *);
+ int (*cache_notify)(struct cache_detail *cd,
+ struct cache_head *h);
+
void (*cache_request)(struct cache_detail *cd,
struct cache_head *ch,
char **bpp, int *blen);
@@ -189,9 +192,9 @@ sunrpc_cache_update(struct cache_detail *detail,
struct cache_head *new, struct cache_head *old, int hash);
extern int
-sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h);
+sunrpc_cache_upcall(struct cache_detail *detail, struct cache_head *h);
extern int
-sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail,
+sunrpc_cache_upcall_warn(struct cache_detail *detail,
struct cache_head *h);
@@ -248,6 +251,14 @@ extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
extern void sunrpc_cache_unregister_pipefs(struct cache_detail *);
extern void sunrpc_cache_unhash(struct cache_detail *, struct cache_head *);
+int sunrpc_cache_requests_count(struct cache_detail *cd);
+int sunrpc_cache_requests_snapshot(struct cache_detail *cd,
+ struct cache_head **items,
+ u64 *seqnos, int max,
+ u64 min_seqno);
+int sunrpc_cache_notify(struct cache_detail *cd, struct cache_head *h,
+ u32 cache_type);
+
/* Must store cache_detail in seq_file->private if using next three functions */
extern void *cache_seq_start_rcu(struct seq_file *file, loff_t *pos);
extern void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos);
diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h
index 43950b5237c8..1cd452ed1db5 100644
--- a/include/linux/sunrpc/gss_krb5.h
+++ b/include/linux/sunrpc/gss_krb5.h
@@ -37,13 +37,9 @@
#ifndef _LINUX_SUNRPC_GSS_KRB5_H
#define _LINUX_SUNRPC_GSS_KRB5_H
-#include <crypto/skcipher.h>
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/gss_err.h>
-/* Length of constant used in key derivation */
-#define GSS_KRB5_K5CLENGTH (5)
-
/* Maximum key length (in bytes) for the supported crypto algorithms */
#define GSS_KRB5_MAX_KEYLEN (32)
@@ -56,11 +52,6 @@
/* The length of the Kerberos GSS token header */
#define GSS_KRB5_TOK_HDR_LEN (16)
-#define KG_TOK_MIC_MSG 0x0101
-#define KG_TOK_WRAP_MSG 0x0201
-
-#define KG2_TOK_INITIAL 0x0101
-#define KG2_TOK_RESPONSE 0x0202
#define KG2_TOK_MIC 0x0404
#define KG2_TOK_WRAP 0x0504
@@ -68,102 +59,6 @@
#define KG2_TOKEN_FLAG_SEALED 0x02
#define KG2_TOKEN_FLAG_ACCEPTORSUBKEY 0x04
-#define KG2_RESP_FLAG_ERROR 0x0001
-#define KG2_RESP_FLAG_DELEG_OK 0x0002
-
-enum sgn_alg {
- SGN_ALG_DES_MAC_MD5 = 0x0000,
- SGN_ALG_MD2_5 = 0x0001,
- SGN_ALG_DES_MAC = 0x0002,
- SGN_ALG_3 = 0x0003, /* not published */
- SGN_ALG_HMAC_SHA1_DES3_KD = 0x0004
-};
-enum seal_alg {
- SEAL_ALG_NONE = 0xffff,
- SEAL_ALG_DES = 0x0000,
- SEAL_ALG_1 = 0x0001, /* not published */
- SEAL_ALG_DES3KD = 0x0002
-};
-
-/*
- * These values are assigned by IANA and published via the
- * subregistry at the link below:
- *
- * https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-2
- */
-#define CKSUMTYPE_CRC32 0x0001
-#define CKSUMTYPE_RSA_MD4 0x0002
-#define CKSUMTYPE_RSA_MD4_DES 0x0003
-#define CKSUMTYPE_DESCBC 0x0004
-#define CKSUMTYPE_RSA_MD5 0x0007
-#define CKSUMTYPE_RSA_MD5_DES 0x0008
-#define CKSUMTYPE_NIST_SHA 0x0009
-#define CKSUMTYPE_HMAC_SHA1_DES3 0x000c
-#define CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f
-#define CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010
-#define CKSUMTYPE_CMAC_CAMELLIA128 0x0011
-#define CKSUMTYPE_CMAC_CAMELLIA256 0x0012
-#define CKSUMTYPE_HMAC_SHA256_128_AES128 0x0013
-#define CKSUMTYPE_HMAC_SHA384_192_AES256 0x0014
-#define CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */
-
-/* from gssapi_err_krb5.h */
-#define KG_CCACHE_NOMATCH (39756032L)
-#define KG_KEYTAB_NOMATCH (39756033L)
-#define KG_TGT_MISSING (39756034L)
-#define KG_NO_SUBKEY (39756035L)
-#define KG_CONTEXT_ESTABLISHED (39756036L)
-#define KG_BAD_SIGN_TYPE (39756037L)
-#define KG_BAD_LENGTH (39756038L)
-#define KG_CTX_INCOMPLETE (39756039L)
-#define KG_CONTEXT (39756040L)
-#define KG_CRED (39756041L)
-#define KG_ENC_DESC (39756042L)
-#define KG_BAD_SEQ (39756043L)
-#define KG_EMPTY_CCACHE (39756044L)
-#define KG_NO_CTYPES (39756045L)
-
-/* per Kerberos v5 protocol spec crypto types from the wire.
- * these get mapped to linux kernel crypto routines.
- *
- * These values are assigned by IANA and published via the
- * subregistry at the link below:
- *
- * https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1
- */
-#define ENCTYPE_NULL 0x0000
-#define ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */
-#define ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */
-#define ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */
-#define ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */
-/* XXX deprecated? */
-#define ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */
-#define ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */
-#define ENCTYPE_DES_HMAC_SHA1 0x0008
-#define ENCTYPE_DES3_CBC_SHA1 0x0010
-#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011
-#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012
-#define ENCTYPE_AES128_CTS_HMAC_SHA256_128 0x0013
-#define ENCTYPE_AES256_CTS_HMAC_SHA384_192 0x0014
-#define ENCTYPE_ARCFOUR_HMAC 0x0017
-#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018
-#define ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019
-#define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001A
-#define ENCTYPE_UNKNOWN 0x01ff
-
-/*
- * Constants used for key derivation
- */
-/* for 3DES */
-#define KG_USAGE_SEAL (22)
-#define KG_USAGE_SIGN (23)
-#define KG_USAGE_SEQ (24)
-
-/* from rfc3961 */
-#define KEY_USAGE_SEED_CHECKSUM (0x99)
-#define KEY_USAGE_SEED_ENCRYPTION (0xAA)
-#define KEY_USAGE_SEED_INTEGRITY (0x55)
-
/* from rfc4121 */
#define KG_USAGE_ACCEPTOR_SEAL (22)
#define KG_USAGE_ACCEPTOR_SIGN (23)
diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h
index df6e08aaad57..5aadb47b3b0e 100644
--- a/include/linux/sunrpc/svc_rdma.h
+++ b/include/linux/sunrpc/svc_rdma.h
@@ -66,7 +66,6 @@ extern unsigned int svcrdma_ord;
extern unsigned int svcrdma_max_requests;
extern unsigned int svcrdma_max_bc_requests;
extern unsigned int svcrdma_max_req_size;
-extern struct workqueue_struct *svcrdma_wq;
extern struct percpu_counter svcrdma_stat_read;
extern struct percpu_counter svcrdma_stat_recv;
@@ -117,6 +116,8 @@ struct svcxprt_rdma {
struct llist_head sc_recv_ctxts;
+ struct llist_head sc_send_release_list;
+
atomic_t sc_completion_ids;
};
/* sc_flags */
@@ -230,13 +231,11 @@ struct svc_rdma_write_info {
unsigned int wi_next_off;
struct svc_rdma_chunk_ctxt wi_cc;
- struct work_struct wi_work;
};
struct svc_rdma_send_ctxt {
struct llist_node sc_node;
struct rpc_rdma_cid sc_cid;
- struct work_struct sc_work;
struct svcxprt_rdma *sc_rdma;
struct ib_send_wr sc_send_wr;
@@ -300,6 +299,7 @@ extern int svc_rdma_process_read_list(struct svcxprt_rdma *rdma,
/* svc_rdma_sendto.c */
extern void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma);
+extern void svc_rdma_send_ctxts_drain(struct svcxprt_rdma *rdma);
extern struct svc_rdma_send_ctxt *
svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma);
extern void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma,
@@ -328,6 +328,7 @@ extern int svc_rdma_result_payload(struct svc_rqst *rqstp, unsigned int offset,
unsigned int length);
/* svc_rdma_transport.c */
+extern void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma);
extern struct svc_xprt_class svc_rdma_class;
#ifdef CONFIG_SUNRPC_BACKCHANNEL
extern struct svc_xprt_class svc_rdma_bc_class;
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index b639a6fafcbc..b102b4f21e6b 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -138,8 +138,23 @@ void xdr_terminate_string(const struct xdr_buf *, const u32);
size_t xdr_buf_pagecount(const struct xdr_buf *buf);
int xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp);
void xdr_free_bvec(struct xdr_buf *buf);
-unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
- const struct xdr_buf *xdr);
+int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+ const struct xdr_buf *xdr);
+int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg, unsigned int nsg);
+int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg_head,
+ unsigned int sg_head_nents,
+ struct scatterlist **sg_overflow, gfp_t gfp);
+
+/*
+ * Inline scatterlist entries for xdr_buf_to_sg_alloc(). Sized to cover the
+ * head kvec, tail kvec, and a few page fragments without any heap allocation.
+ */
+enum {
+ XDR_BUF_TO_SG_NENTS = 8,
+};
+
static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int len)
{
@@ -260,7 +275,6 @@ extern void xdr_finish_decode(struct xdr_stream *xdr);
extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
-extern int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data);
extern void xdr_set_pagelen(struct xdr_stream *, unsigned int len);
extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
unsigned int len);
diff --git a/include/linux/sunrpc/xdrgen/nlm3.h b/include/linux/sunrpc/xdrgen/nlm3.h
new file mode 100644
index 000000000000..897e7d91807c
--- /dev/null
+++ b/include/linux/sunrpc/xdrgen/nlm3.h
@@ -0,0 +1,210 @@
+/* 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 */
+
+#ifndef _LINUX_XDRGEN_NLM3_DEF_H
+#define _LINUX_XDRGEN_NLM3_DEF_H
+
+#include <linux/types.h>
+#include <linux/sunrpc/xdrgen/_defs.h>
+
+enum { LM_MAXSTRLEN = 1024 };
+
+enum { LM_MAXNAMELEN = 1025 };
+
+enum { MAXNETOBJ_SZ = 1024 };
+
+typedef opaque netobj;
+
+enum nlm_stats {
+ LCK_GRANTED = 0,
+ LCK_DENIED = 1,
+ LCK_DENIED_NOLOCKS = 2,
+ LCK_BLOCKED = 3,
+ LCK_DENIED_GRACE_PERIOD = 4,
+};
+
+typedef __be32 nlm_stats;
+
+struct nlm_stat {
+ nlm_stats stat;
+};
+
+struct nlm_res {
+ netobj cookie;
+ struct nlm_stat stat;
+};
+
+struct nlm_holder {
+ bool exclusive;
+ s32 uppid;
+ netobj oh;
+ u32 l_offset;
+ u32 l_len;
+};
+
+struct nlm_testrply {
+ nlm_stats stat;
+ union {
+ struct nlm_holder holder;
+ } u;
+};
+
+struct nlm_testres {
+ netobj cookie;
+ struct nlm_testrply test_stat;
+};
+
+struct nlm_lock {
+ string caller_name;
+ netobj fh;
+ netobj oh;
+ s32 uppid;
+ u32 l_offset;
+ u32 l_len;
+};
+
+struct nlm_lockargs {
+ netobj cookie;
+ bool block;
+ bool exclusive;
+ struct nlm_lock alock;
+ bool reclaim;
+ s32 state;
+};
+
+struct nlm_cancargs {
+ netobj cookie;
+ bool block;
+ bool exclusive;
+ struct nlm_lock alock;
+};
+
+struct nlm_testargs {
+ netobj cookie;
+ bool exclusive;
+ struct nlm_lock alock;
+};
+
+struct nlm_unlockargs {
+ netobj cookie;
+ struct nlm_lock alock;
+};
+
+enum fsh_mode {
+ fsm_DN = 0,
+ fsm_DR = 1,
+ fsm_DW = 2,
+ fsm_DRW = 3,
+};
+
+typedef enum fsh_mode fsh_mode;
+
+enum fsh_access {
+ fsa_NONE = 0,
+ fsa_R = 1,
+ fsa_W = 2,
+ fsa_RW = 3,
+};
+
+typedef enum fsh_access fsh_access;
+
+struct nlm_share {
+ string caller_name;
+ netobj fh;
+ netobj oh;
+ fsh_mode mode;
+ fsh_access access;
+};
+
+struct nlm_shareargs {
+ netobj cookie;
+ struct nlm_share share;
+ bool reclaim;
+};
+
+struct nlm_shareres {
+ netobj cookie;
+ nlm_stats stat;
+ s32 sequence;
+};
+
+struct nlm_notify {
+ string name;
+ s32 state;
+};
+
+enum { SM_PRIV_SIZE = 16 };
+
+struct nlm_notifyargs {
+ struct nlm_notify notify;
+ u8 private[SM_PRIV_SIZE];
+};
+
+enum {
+ NLM_NULL = 0,
+ NLM_TEST = 1,
+ NLM_LOCK = 2,
+ NLM_CANCEL = 3,
+ NLM_UNLOCK = 4,
+ NLM_GRANTED = 5,
+ NLM_TEST_MSG = 6,
+ NLM_LOCK_MSG = 7,
+ NLM_CANCEL_MSG = 8,
+ NLM_UNLOCK_MSG = 9,
+ NLM_GRANTED_MSG = 10,
+ NLM_TEST_RES = 11,
+ NLM_LOCK_RES = 12,
+ NLM_CANCEL_RES = 13,
+ NLM_UNLOCK_RES = 14,
+ NLM_GRANTED_RES = 15,
+ NLM_SM_NOTIFY = 16,
+ NLM_SHARE = 20,
+ NLM_UNSHARE = 21,
+ NLM_NM_LOCK = 22,
+ NLM_FREE_ALL = 23,
+};
+
+#ifndef NLM_PROG
+#define NLM_PROG (100021)
+#endif
+
+#define NLM3_netobj_sz (XDR_unsigned_int + XDR_QUADLEN(MAXNETOBJ_SZ))
+#define NLM3_nlm_stats_sz (XDR_int)
+#define NLM3_nlm_stat_sz \
+ (NLM3_nlm_stats_sz)
+#define NLM3_nlm_res_sz \
+ (NLM3_netobj_sz + NLM3_nlm_stat_sz)
+#define NLM3_nlm_holder_sz \
+ (XDR_bool + XDR_int + NLM3_netobj_sz + XDR_unsigned_int + XDR_unsigned_int)
+#define NLM3_nlm_testrply_sz \
+ (NLM3_nlm_stats_sz + NLM3_nlm_holder_sz)
+#define NLM3_nlm_testres_sz \
+ (NLM3_netobj_sz + NLM3_nlm_testrply_sz)
+#define NLM3_nlm_lock_sz \
+ (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM3_netobj_sz + NLM3_netobj_sz + XDR_int + XDR_unsigned_int + XDR_unsigned_int)
+#define NLM3_nlm_lockargs_sz \
+ (NLM3_netobj_sz + XDR_bool + XDR_bool + NLM3_nlm_lock_sz + XDR_bool + XDR_int)
+#define NLM3_nlm_cancargs_sz \
+ (NLM3_netobj_sz + XDR_bool + XDR_bool + NLM3_nlm_lock_sz)
+#define NLM3_nlm_testargs_sz \
+ (NLM3_netobj_sz + XDR_bool + NLM3_nlm_lock_sz)
+#define NLM3_nlm_unlockargs_sz \
+ (NLM3_netobj_sz + NLM3_nlm_lock_sz)
+#define NLM3_fsh_mode_sz (XDR_int)
+#define NLM3_fsh_access_sz (XDR_int)
+#define NLM3_nlm_share_sz \
+ (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM3_netobj_sz + NLM3_netobj_sz + NLM3_fsh_mode_sz + NLM3_fsh_access_sz)
+#define NLM3_nlm_shareargs_sz \
+ (NLM3_netobj_sz + NLM3_nlm_share_sz + XDR_bool)
+#define NLM3_nlm_shareres_sz \
+ (NLM3_netobj_sz + NLM3_nlm_stats_sz + XDR_int)
+#define NLM3_nlm_notify_sz \
+ (XDR_unsigned_int + XDR_QUADLEN(LM_MAXNAMELEN) + XDR_long)
+#define NLM3_nlm_notifyargs_sz \
+ (NLM3_nlm_notify_sz + XDR_QUADLEN(SM_PRIV_SIZE))
+#define NLM3_MAX_ARGS_SZ \
+ (NLM3_nlm_lockargs_sz)
+
+#endif /* _LINUX_XDRGEN_NLM3_DEF_H */
diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
index 97c7447f4d14..f5b75d5caba9 100644
--- a/include/uapi/linux/nfsd_netlink.h
+++ b/include/uapi/linux/nfsd_netlink.h
@@ -10,6 +10,53 @@
#define NFSD_FAMILY_NAME "nfsd"
#define NFSD_FAMILY_VERSION 1
+enum nfsd_cache_type {
+ NFSD_CACHE_TYPE_SVC_EXPORT = 1,
+ NFSD_CACHE_TYPE_EXPKEY = 2,
+};
+
+/*
+ * These flags are ordered to match the NFSEXP_* flags in
+ * include/linux/nfsd/export.h
+ */
+enum nfsd_export_flags {
+ NFSD_EXPORT_FLAGS_READONLY = 1,
+ NFSD_EXPORT_FLAGS_INSECURE_PORT = 2,
+ NFSD_EXPORT_FLAGS_ROOTSQUASH = 4,
+ NFSD_EXPORT_FLAGS_ALLSQUASH = 8,
+ NFSD_EXPORT_FLAGS_ASYNC = 16,
+ NFSD_EXPORT_FLAGS_GATHERED_WRITES = 32,
+ NFSD_EXPORT_FLAGS_NOREADDIRPLUS = 64,
+ NFSD_EXPORT_FLAGS_SECURITY_LABEL = 128,
+ NFSD_EXPORT_FLAGS_SIGN_FH = 256,
+ NFSD_EXPORT_FLAGS_NOHIDE = 512,
+ NFSD_EXPORT_FLAGS_NOSUBTREECHECK = 1024,
+ NFSD_EXPORT_FLAGS_NOAUTHNLM = 2048,
+ NFSD_EXPORT_FLAGS_MSNFS = 4096,
+ NFSD_EXPORT_FLAGS_FSID = 8192,
+ NFSD_EXPORT_FLAGS_CROSSMOUNT = 16384,
+ NFSD_EXPORT_FLAGS_NOACL = 32768,
+ NFSD_EXPORT_FLAGS_V4ROOT = 65536,
+ NFSD_EXPORT_FLAGS_PNFS = 131072,
+};
+
+/*
+ * These flags are ordered to match the NFSEXP_XPRTSEC_* flags in
+ * include/linux/nfsd/export.h
+ */
+enum nfsd_xprtsec_mode {
+ NFSD_XPRTSEC_MODE_NONE = 1,
+ NFSD_XPRTSEC_MODE_TLS = 2,
+ NFSD_XPRTSEC_MODE_MTLS = 4,
+};
+
+enum {
+ NFSD_A_CACHE_NOTIFY_CACHE_TYPE = 1,
+
+ __NFSD_A_CACHE_NOTIFY_MAX,
+ NFSD_A_CACHE_NOTIFY_MAX = (__NFSD_A_CACHE_NOTIFY_MAX - 1)
+};
+
enum {
NFSD_A_RPC_STATUS_XID = 1,
NFSD_A_RPC_STATUS_FLAGS,
@@ -82,6 +129,103 @@ enum {
};
enum {
+ NFSD_A_FSLOCATION_HOST = 1,
+ NFSD_A_FSLOCATION_PATH,
+
+ __NFSD_A_FSLOCATION_MAX,
+ NFSD_A_FSLOCATION_MAX = (__NFSD_A_FSLOCATION_MAX - 1)
+};
+
+enum {
+ NFSD_A_FSLOCATIONS_LOCATION = 1,
+
+ __NFSD_A_FSLOCATIONS_MAX,
+ NFSD_A_FSLOCATIONS_MAX = (__NFSD_A_FSLOCATIONS_MAX - 1)
+};
+
+enum {
+ NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR = 1,
+ NFSD_A_AUTH_FLAVOR_FLAGS,
+
+ __NFSD_A_AUTH_FLAVOR_MAX,
+ NFSD_A_AUTH_FLAVOR_MAX = (__NFSD_A_AUTH_FLAVOR_MAX - 1)
+};
+
+enum {
+ NFSD_A_SVC_EXPORT_SEQNO = 1,
+ NFSD_A_SVC_EXPORT_CLIENT,
+ NFSD_A_SVC_EXPORT_PATH,
+ NFSD_A_SVC_EXPORT_NEGATIVE,
+ NFSD_A_SVC_EXPORT_EXPIRY,
+ NFSD_A_SVC_EXPORT_ANON_UID,
+ NFSD_A_SVC_EXPORT_ANON_GID,
+ NFSD_A_SVC_EXPORT_FSLOCATIONS,
+ NFSD_A_SVC_EXPORT_UUID,
+ NFSD_A_SVC_EXPORT_SECINFO,
+ NFSD_A_SVC_EXPORT_XPRTSEC,
+ NFSD_A_SVC_EXPORT_FLAGS,
+ NFSD_A_SVC_EXPORT_FSID,
+
+ __NFSD_A_SVC_EXPORT_MAX,
+ NFSD_A_SVC_EXPORT_MAX = (__NFSD_A_SVC_EXPORT_MAX - 1)
+};
+
+enum {
+ NFSD_A_SVC_EXPORT_REQS_REQUESTS = 1,
+
+ __NFSD_A_SVC_EXPORT_REQS_MAX,
+ NFSD_A_SVC_EXPORT_REQS_MAX = (__NFSD_A_SVC_EXPORT_REQS_MAX - 1)
+};
+
+enum {
+ NFSD_A_EXPKEY_SEQNO = 1,
+ NFSD_A_EXPKEY_CLIENT,
+ NFSD_A_EXPKEY_FSIDTYPE,
+ NFSD_A_EXPKEY_FSID,
+ NFSD_A_EXPKEY_NEGATIVE,
+ NFSD_A_EXPKEY_EXPIRY,
+ NFSD_A_EXPKEY_PATH,
+
+ __NFSD_A_EXPKEY_MAX,
+ NFSD_A_EXPKEY_MAX = (__NFSD_A_EXPKEY_MAX - 1)
+};
+
+enum {
+ NFSD_A_EXPKEY_REQS_REQUESTS = 1,
+
+ __NFSD_A_EXPKEY_REQS_MAX,
+ NFSD_A_EXPKEY_REQS_MAX = (__NFSD_A_EXPKEY_REQS_MAX - 1)
+};
+
+enum {
+ NFSD_A_CACHE_FLUSH_MASK = 1,
+
+ __NFSD_A_CACHE_FLUSH_MAX,
+ NFSD_A_CACHE_FLUSH_MAX = (__NFSD_A_CACHE_FLUSH_MAX - 1)
+};
+
+enum {
+ NFSD_A_UNLOCK_IP_ADDRESS = 1,
+
+ __NFSD_A_UNLOCK_IP_MAX,
+ NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1)
+};
+
+enum {
+ NFSD_A_UNLOCK_FILESYSTEM_PATH = 1,
+
+ __NFSD_A_UNLOCK_FILESYSTEM_MAX,
+ NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1)
+};
+
+enum {
+ NFSD_A_UNLOCK_EXPORT_PATH = 1,
+
+ __NFSD_A_UNLOCK_EXPORT_MAX,
+ NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1)
+};
+
+enum {
NFSD_CMD_RPC_STATUS_GET = 1,
NFSD_CMD_THREADS_SET,
NFSD_CMD_THREADS_GET,
@@ -91,9 +235,21 @@ enum {
NFSD_CMD_LISTENER_GET,
NFSD_CMD_POOL_MODE_SET,
NFSD_CMD_POOL_MODE_GET,
+ NFSD_CMD_CACHE_NOTIFY,
+ NFSD_CMD_SVC_EXPORT_GET_REQS,
+ NFSD_CMD_SVC_EXPORT_SET_REQS,
+ NFSD_CMD_EXPKEY_GET_REQS,
+ NFSD_CMD_EXPKEY_SET_REQS,
+ NFSD_CMD_CACHE_FLUSH,
+ NFSD_CMD_UNLOCK_IP,
+ NFSD_CMD_UNLOCK_FILESYSTEM,
+ NFSD_CMD_UNLOCK_EXPORT,
__NFSD_CMD_MAX,
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
};
+#define NFSD_MCGRP_NONE "none"
+#define NFSD_MCGRP_EXPORTD "exportd"
+
#endif /* _UAPI_LINUX_NFSD_NETLINK_H */
diff --git a/include/uapi/linux/sunrpc_netlink.h b/include/uapi/linux/sunrpc_netlink.h
new file mode 100644
index 000000000000..34677f0ec2f9
--- /dev/null
+++ b/include/uapi/linux/sunrpc_netlink.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/sunrpc_cache.yaml */
+/* YNL-GEN uapi header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _UAPI_LINUX_SUNRPC_NETLINK_H
+#define _UAPI_LINUX_SUNRPC_NETLINK_H
+
+#define SUNRPC_FAMILY_NAME "sunrpc"
+#define SUNRPC_FAMILY_VERSION 1
+
+enum sunrpc_cache_type {
+ SUNRPC_CACHE_TYPE_IP_MAP = 1,
+ SUNRPC_CACHE_TYPE_UNIX_GID = 2,
+};
+
+enum {
+ SUNRPC_A_CACHE_NOTIFY_CACHE_TYPE = 1,
+
+ __SUNRPC_A_CACHE_NOTIFY_MAX,
+ SUNRPC_A_CACHE_NOTIFY_MAX = (__SUNRPC_A_CACHE_NOTIFY_MAX - 1)
+};
+
+enum {
+ SUNRPC_A_IP_MAP_SEQNO = 1,
+ SUNRPC_A_IP_MAP_CLASS,
+ SUNRPC_A_IP_MAP_ADDR,
+ SUNRPC_A_IP_MAP_DOMAIN,
+ SUNRPC_A_IP_MAP_NEGATIVE,
+ SUNRPC_A_IP_MAP_EXPIRY,
+
+ __SUNRPC_A_IP_MAP_MAX,
+ SUNRPC_A_IP_MAP_MAX = (__SUNRPC_A_IP_MAP_MAX - 1)
+};
+
+enum {
+ SUNRPC_A_IP_MAP_REQS_REQUESTS = 1,
+
+ __SUNRPC_A_IP_MAP_REQS_MAX,
+ SUNRPC_A_IP_MAP_REQS_MAX = (__SUNRPC_A_IP_MAP_REQS_MAX - 1)
+};
+
+enum {
+ SUNRPC_A_UNIX_GID_SEQNO = 1,
+ SUNRPC_A_UNIX_GID_UID,
+ SUNRPC_A_UNIX_GID_GIDS,
+ SUNRPC_A_UNIX_GID_NEGATIVE,
+ SUNRPC_A_UNIX_GID_EXPIRY,
+
+ __SUNRPC_A_UNIX_GID_MAX,
+ SUNRPC_A_UNIX_GID_MAX = (__SUNRPC_A_UNIX_GID_MAX - 1)
+};
+
+enum {
+ SUNRPC_A_UNIX_GID_REQS_REQUESTS = 1,
+
+ __SUNRPC_A_UNIX_GID_REQS_MAX,
+ SUNRPC_A_UNIX_GID_REQS_MAX = (__SUNRPC_A_UNIX_GID_REQS_MAX - 1)
+};
+
+enum {
+ SUNRPC_A_CACHE_FLUSH_MASK = 1,
+
+ __SUNRPC_A_CACHE_FLUSH_MAX,
+ SUNRPC_A_CACHE_FLUSH_MAX = (__SUNRPC_A_CACHE_FLUSH_MAX - 1)
+};
+
+enum {
+ SUNRPC_CMD_CACHE_NOTIFY = 1,
+ SUNRPC_CMD_IP_MAP_GET_REQS,
+ SUNRPC_CMD_IP_MAP_SET_REQS,
+ SUNRPC_CMD_UNIX_GID_GET_REQS,
+ SUNRPC_CMD_UNIX_GID_SET_REQS,
+ SUNRPC_CMD_CACHE_FLUSH,
+
+ __SUNRPC_CMD_MAX,
+ SUNRPC_CMD_MAX = (__SUNRPC_CMD_MAX - 1)
+};
+
+#define SUNRPC_MCGRP_NONE "none"
+#define SUNRPC_MCGRP_EXPORTD "exportd"
+
+#endif /* _UAPI_LINUX_SUNRPC_NETLINK_H */
diff --git a/net/sunrpc/.kunitconfig b/net/sunrpc/.kunitconfig
deleted file mode 100644
index eb02b906c295..000000000000
--- a/net/sunrpc/.kunitconfig
+++ /dev/null
@@ -1,29 +0,0 @@
-CONFIG_KUNIT=y
-CONFIG_UBSAN=y
-CONFIG_STACKTRACE=y
-CONFIG_NET=y
-CONFIG_NETWORK_FILESYSTEMS=y
-CONFIG_INET=y
-CONFIG_FILE_LOCKING=y
-CONFIG_MULTIUSER=y
-CONFIG_CRYPTO=y
-CONFIG_CRYPTO_CBC=y
-CONFIG_CRYPTO_CTS=y
-CONFIG_CRYPTO_ECB=y
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_CMAC=y
-CONFIG_CRYPTO_MD5=y
-CONFIG_CRYPTO_SHA1=y
-CONFIG_CRYPTO_SHA256=y
-CONFIG_CRYPTO_SHA512=y
-CONFIG_CRYPTO_DES=y
-CONFIG_CRYPTO_AES=y
-CONFIG_CRYPTO_CAMELLIA=y
-CONFIG_NFS_FS=y
-CONFIG_SUNRPC=y
-CONFIG_SUNRPC_GSS=y
-CONFIG_RPCSEC_GSS_KRB5=y
-CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1=y
-CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA=y
-CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2=y
-CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST=y
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index a570e7adf270..e7808e5714dc 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -21,8 +21,7 @@ config RPCSEC_GSS_KRB5
depends on SUNRPC && CRYPTO
default y
select SUNRPC_GSS
- select CRYPTO_SKCIPHER
- select CRYPTO_HASH
+ select CRYPTO_KRB5
help
Choose Y here to enable Secure RPC using the Kerberos version 5
GSS-API mechanism (RFC 1964).
@@ -34,59 +33,6 @@ config RPCSEC_GSS_KRB5
If unsure, say Y.
-config RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1
- bool "Enable Kerberos enctypes based on AES and SHA-1"
- depends on RPCSEC_GSS_KRB5
- depends on CRYPTO_CBC && CRYPTO_CTS
- depends on CRYPTO_HMAC && CRYPTO_SHA1
- depends on CRYPTO_AES
- default y
- help
- Choose Y to enable the use of Kerberos 5 encryption types
- that utilize Advanced Encryption Standard (AES) ciphers and
- SHA-1 digests. These include aes128-cts-hmac-sha1-96 and
- aes256-cts-hmac-sha1-96.
-
-config RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA
- bool "Enable Kerberos encryption types based on Camellia and CMAC"
- depends on RPCSEC_GSS_KRB5
- depends on CRYPTO_CBC && CRYPTO_CTS && CRYPTO_CAMELLIA
- depends on CRYPTO_CMAC
- default n
- help
- Choose Y to enable the use of Kerberos 5 encryption types
- that utilize Camellia ciphers (RFC 3713) and CMAC digests
- (NIST Special Publication 800-38B). These include
- camellia128-cts-cmac and camellia256-cts-cmac.
-
-config RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2
- bool "Enable Kerberos enctypes based on AES and SHA-2"
- depends on RPCSEC_GSS_KRB5
- depends on CRYPTO_CBC && CRYPTO_CTS
- depends on CRYPTO_HMAC && CRYPTO_SHA256 && CRYPTO_SHA512
- depends on CRYPTO_AES
- default n
- help
- Choose Y to enable the use of Kerberos 5 encryption types
- that utilize Advanced Encryption Standard (AES) ciphers and
- SHA-2 digests. These include aes128-cts-hmac-sha256-128 and
- aes256-cts-hmac-sha384-192.
-
-config RPCSEC_GSS_KRB5_KUNIT_TEST
- tristate "KUnit tests for RPCSEC GSS Kerberos" if !KUNIT_ALL_TESTS
- depends on RPCSEC_GSS_KRB5 && KUNIT
- default KUNIT_ALL_TESTS
- help
- This builds the KUnit tests for RPCSEC GSS Kerberos 5.
-
- KUnit tests run during boot and output the results to the debug
- log in TAP format (https://testanything.org/). Only useful for
- kernel devs running KUnit test harness and are not for inclusion
- into a production build.
-
- For more information on KUnit and unit tests in general, refer
- to the KUnit documentation in Documentation/dev-tools/kunit/.
-
config SUNRPC_DEBUG
bool "RPC: Enable dprintk debugging"
depends on SUNRPC && SYSCTL
diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
index f89c10fe7e6a..96727df3aa85 100644
--- a/net/sunrpc/Makefile
+++ b/net/sunrpc/Makefile
@@ -14,7 +14,7 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
addr.o rpcb_clnt.o timer.o xdr.o \
sunrpc_syms.o cache.o rpc_pipe.o sysfs.o \
svc_xprt.o \
- xprtmultipath.o
+ xprtmultipath.o netlink.o
sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o
sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o
sunrpc-$(CONFIG_PROC_FS) += stats.o
diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile
index 452f67deebc6..68676389d65f 100644
--- a/net/sunrpc/auth_gss/Makefile
+++ b/net/sunrpc/auth_gss/Makefile
@@ -12,6 +12,4 @@ auth_rpcgss-y := auth_gss.o \
obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
rpcsec_gss_krb5-y := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
- gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o
-
-obj-$(CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST) += gss_krb5_test.o
+ gss_krb5_wrap.o gss_krb5_crypto.o
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index 16dcf115de1e..cf461ebcdde5 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -34,19 +34,14 @@
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#include <crypto/hash.h>
-#include <crypto/skcipher.h>
-#include <crypto/utils.h>
#include <linux/err.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
-#include <linux/random.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
-#include <kunit/visibility.h>
#include "gss_krb5_internal.h"
@@ -54,303 +49,6 @@
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
-/**
- * krb5_make_confounder - Generate a confounder string
- * @p: memory location into which to write the string
- * @conflen: string length to write, in octets
- *
- * RFCs 1964 and 3961 mention only "a random confounder" without going
- * into detail about its function or cryptographic requirements. The
- * assumed purpose is to prevent repeated encryption of a plaintext with
- * the same key from generating the same ciphertext. It is also used to
- * pad minimum plaintext length to at least a single cipher block.
- *
- * However, in situations like the GSS Kerberos 5 mechanism, where the
- * encryption IV is always all zeroes, the confounder also effectively
- * functions like an IV. Thus, not only must it be unique from message
- * to message, but it must also be difficult to predict. Otherwise an
- * attacker can correlate the confounder to previous or future values,
- * making the encryption easier to break.
- *
- * Given that the primary consumer of this encryption mechanism is a
- * network storage protocol, a type of traffic that often carries
- * predictable payloads (eg, all zeroes when reading unallocated blocks
- * from a file), our confounder generation has to be cryptographically
- * strong.
- */
-void krb5_make_confounder(u8 *p, int conflen)
-{
- get_random_bytes(p, conflen);
-}
-
-/**
- * krb5_encrypt - simple encryption of an RPCSEC GSS payload
- * @tfm: initialized cipher transform
- * @iv: pointer to an IV
- * @in: plaintext to encrypt
- * @out: OUT: ciphertext
- * @length: length of input and output buffers, in bytes
- *
- * @iv may be NULL to force the use of an all-zero IV.
- * The buffer containing the IV must be as large as the
- * cipher's ivsize.
- *
- * Return values:
- * %0: @in successfully encrypted into @out
- * negative errno: @in not encrypted
- */
-u32
-krb5_encrypt(
- struct crypto_sync_skcipher *tfm,
- void * iv,
- void * in,
- void * out,
- int length)
-{
- u32 ret = -EINVAL;
- struct scatterlist sg[1];
- u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0};
- SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
-
- if (length % crypto_sync_skcipher_blocksize(tfm) != 0)
- goto out;
-
- if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
- dprintk("RPC: gss_k5encrypt: tfm iv size too large %d\n",
- crypto_sync_skcipher_ivsize(tfm));
- goto out;
- }
-
- if (iv)
- memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
-
- memcpy(out, in, length);
- sg_init_one(sg, out, length);
-
- skcipher_request_set_sync_tfm(req, tfm);
- skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, sg, sg, length, local_iv);
-
- ret = crypto_skcipher_encrypt(req);
- skcipher_request_zero(req);
-out:
- dprintk("RPC: krb5_encrypt returns %d\n", ret);
- return ret;
-}
-
-static int
-checksummer(struct scatterlist *sg, void *data)
-{
- struct ahash_request *req = data;
-
- ahash_request_set_crypt(req, sg, NULL, sg->length);
-
- return crypto_ahash_update(req);
-}
-
-/**
- * gss_krb5_checksum - Compute the MAC for a GSS Wrap or MIC token
- * @tfm: an initialized hash transform
- * @header: pointer to a buffer containing the token header, or NULL
- * @hdrlen: number of octets in @header
- * @body: xdr_buf containing an RPC message (body.len is the message length)
- * @body_offset: byte offset into @body to start checksumming
- * @cksumout: OUT: a buffer to be filled in with the computed HMAC
- *
- * Usually expressed as H = HMAC(K, message)[1..h] .
- *
- * Caller provides the truncation length of the output token (h) in
- * cksumout.len.
- *
- * Return values:
- * %GSS_S_COMPLETE: Digest computed, @cksumout filled in
- * %GSS_S_FAILURE: Call failed
- */
-u32
-gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
- const struct xdr_buf *body, int body_offset,
- struct xdr_netobj *cksumout)
-{
- struct ahash_request *req;
- int err = -ENOMEM;
- u8 *checksumdata;
-
- checksumdata = kmalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL);
- if (!checksumdata)
- return GSS_S_FAILURE;
-
- req = ahash_request_alloc(tfm, GFP_KERNEL);
- if (!req)
- goto out_free_cksum;
- ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
- err = crypto_ahash_init(req);
- if (err)
- goto out_free_ahash;
-
- /*
- * Per RFC 4121 Section 4.2.4, the checksum is performed over the
- * data body first, then over the octets in "header".
- */
- err = xdr_process_buf(body, body_offset, body->len - body_offset,
- checksummer, req);
- if (err)
- goto out_free_ahash;
- if (header) {
- struct scatterlist sg[1];
-
- sg_init_one(sg, header, hdrlen);
- ahash_request_set_crypt(req, sg, NULL, hdrlen);
- err = crypto_ahash_update(req);
- if (err)
- goto out_free_ahash;
- }
-
- ahash_request_set_crypt(req, NULL, checksumdata, 0);
- err = crypto_ahash_final(req);
- if (err)
- goto out_free_ahash;
-
- memcpy(cksumout->data, checksumdata,
- min_t(int, cksumout->len, crypto_ahash_digestsize(tfm)));
-
-out_free_ahash:
- ahash_request_free(req);
-out_free_cksum:
- kfree_sensitive(checksumdata);
- return err ? GSS_S_FAILURE : GSS_S_COMPLETE;
-}
-EXPORT_SYMBOL_IF_KUNIT(gss_krb5_checksum);
-
-struct encryptor_desc {
- u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
- struct skcipher_request *req;
- int pos;
- struct xdr_buf *outbuf;
- struct page **pages;
- struct scatterlist infrags[4];
- struct scatterlist outfrags[4];
- int fragno;
- int fraglen;
-};
-
-static int
-encryptor(struct scatterlist *sg, void *data)
-{
- struct encryptor_desc *desc = data;
- struct xdr_buf *outbuf = desc->outbuf;
- struct crypto_sync_skcipher *tfm =
- crypto_sync_skcipher_reqtfm(desc->req);
- struct page *in_page;
- int thislen = desc->fraglen + sg->length;
- int fraglen, ret;
- int page_pos;
-
- /* Worst case is 4 fragments: head, end of page 1, start
- * of page 2, tail. Anything more is a bug. */
- BUG_ON(desc->fragno > 3);
-
- page_pos = desc->pos - outbuf->head[0].iov_len;
- if (page_pos >= 0 && page_pos < outbuf->page_len) {
- /* pages are not in place: */
- int i = (page_pos + outbuf->page_base) >> PAGE_SHIFT;
- in_page = desc->pages[i];
- } else {
- in_page = sg_page(sg);
- }
- sg_set_page(&desc->infrags[desc->fragno], in_page, sg->length,
- sg->offset);
- sg_set_page(&desc->outfrags[desc->fragno], sg_page(sg), sg->length,
- sg->offset);
- desc->fragno++;
- desc->fraglen += sg->length;
- desc->pos += sg->length;
-
- fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1);
- thislen -= fraglen;
-
- if (thislen == 0)
- return 0;
-
- sg_mark_end(&desc->infrags[desc->fragno - 1]);
- sg_mark_end(&desc->outfrags[desc->fragno - 1]);
-
- skcipher_request_set_crypt(desc->req, desc->infrags, desc->outfrags,
- thislen, desc->iv);
-
- ret = crypto_skcipher_encrypt(desc->req);
- if (ret)
- return ret;
-
- sg_init_table(desc->infrags, 4);
- sg_init_table(desc->outfrags, 4);
-
- if (fraglen) {
- sg_set_page(&desc->outfrags[0], sg_page(sg), fraglen,
- sg->offset + sg->length - fraglen);
- desc->infrags[0] = desc->outfrags[0];
- sg_assign_page(&desc->infrags[0], in_page);
- desc->fragno = 1;
- desc->fraglen = fraglen;
- } else {
- desc->fragno = 0;
- desc->fraglen = 0;
- }
- return 0;
-}
-
-struct decryptor_desc {
- u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
- struct skcipher_request *req;
- struct scatterlist frags[4];
- int fragno;
- int fraglen;
-};
-
-static int
-decryptor(struct scatterlist *sg, void *data)
-{
- struct decryptor_desc *desc = data;
- int thislen = desc->fraglen + sg->length;
- struct crypto_sync_skcipher *tfm =
- crypto_sync_skcipher_reqtfm(desc->req);
- int fraglen, ret;
-
- /* Worst case is 4 fragments: head, end of page 1, start
- * of page 2, tail. Anything more is a bug. */
- BUG_ON(desc->fragno > 3);
- sg_set_page(&desc->frags[desc->fragno], sg_page(sg), sg->length,
- sg->offset);
- desc->fragno++;
- desc->fraglen += sg->length;
-
- fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1);
- thislen -= fraglen;
-
- if (thislen == 0)
- return 0;
-
- sg_mark_end(&desc->frags[desc->fragno - 1]);
-
- skcipher_request_set_crypt(desc->req, desc->frags, desc->frags,
- thislen, desc->iv);
-
- ret = crypto_skcipher_decrypt(desc->req);
- if (ret)
- return ret;
-
- sg_init_table(desc->frags, 4);
-
- if (fraglen) {
- sg_set_page(&desc->frags[0], sg_page(sg), fraglen,
- sg->offset + sg->length - fraglen);
- desc->fragno = 1;
- desc->fraglen = fraglen;
- } else {
- desc->fragno = 0;
- desc->fraglen = 0;
- }
- return 0;
-}
/*
* This function makes the assumption that it was ultimately called
@@ -363,7 +61,7 @@ decryptor(struct scatterlist *sg, void *data)
*
* Even with that guarantee, this function may be called more than
* once in the processing of gss_wrap(). The best we can do is
- * verify at compile-time (see GSS_KRB5_SLACK_CHECK) that the
+ * verify at compile-time (see GSS_KRB5_MAX_SLACK_NEEDED) that the
* largest expected shift will fit within RPC_MAX_AUTH_SIZE.
* At run-time we can verify that a single invocation of this
* function doesn't attempt to use more the RPC_MAX_AUTH_SIZE.
@@ -389,567 +87,235 @@ xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen)
return 0;
}
-static u32
-gss_krb5_cts_crypt(struct crypto_sync_skcipher *cipher, struct xdr_buf *buf,
- u32 offset, u8 *iv, struct page **pages, int encrypt)
-{
- u32 ret;
- struct scatterlist sg[1];
- SYNC_SKCIPHER_REQUEST_ON_STACK(req, cipher);
- u8 *data;
- struct page **save_pages;
- u32 len = buf->len - offset;
-
- if (len > GSS_KRB5_MAX_BLOCKSIZE * 2) {
- WARN_ON(0);
- return -ENOMEM;
- }
- data = kmalloc(GSS_KRB5_MAX_BLOCKSIZE * 2, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- /*
- * For encryption, we want to read from the cleartext
- * page cache pages, and write the encrypted data to
- * the supplied xdr_buf pages.
- */
- save_pages = buf->pages;
- if (encrypt)
- buf->pages = pages;
-
- ret = read_bytes_from_xdr_buf(buf, offset, data, len);
- buf->pages = save_pages;
- if (ret)
- goto out;
-
- sg_init_one(sg, data, len);
-
- skcipher_request_set_sync_tfm(req, cipher);
- skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, sg, sg, len, iv);
-
- if (encrypt)
- ret = crypto_skcipher_encrypt(req);
- else
- ret = crypto_skcipher_decrypt(req);
-
- skcipher_request_zero(req);
-
- if (ret)
- goto out;
-
- ret = write_bytes_to_xdr_buf(buf, offset, data, len);
-
-#if IS_ENABLED(CONFIG_KUNIT)
- /*
- * CBC-CTS does not define an output IV but RFC 3962 defines it as the
- * penultimate block of ciphertext, so copy that into the IV buffer
- * before returning.
- */
- if (encrypt)
- memcpy(iv, data, crypto_sync_skcipher_ivsize(cipher));
-#endif
-
-out:
- kfree(data);
- return ret;
-}
/**
- * krb5_cbc_cts_encrypt - encrypt in CBC mode with CTS
- * @cts_tfm: CBC cipher with CTS
- * @cbc_tfm: base CBC cipher
- * @offset: starting byte offset for plaintext
- * @buf: OUT: output buffer
- * @pages: plaintext
- * @iv: output CBC initialization vector, or NULL
- * @ivsize: size of @iv, in octets
+ * gss_krb5_aead_encrypt - Encrypt a wrap token using crypto/krb5
+ * @kctx: Kerberos context
+ * @offset: byte offset of the GSS token header in @buf
+ * @buf: OUT: send buffer
+ * @pages: plaintext payload pages (page cache data)
*
- * To provide confidentiality, encrypt using cipher block chaining
- * with ciphertext stealing. Message integrity is handled separately.
+ * The xdr_buf setup mirrors the original per-enctype encrypt
+ * functions, but the CBC-CTS encryption and HMAC are replaced
+ * by a single AEAD operation through the crypto/krb5 library.
*
* Return values:
- * %0: encryption successful
- * negative errno: encryption could not be completed
- */
-VISIBLE_IF_KUNIT
-int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm,
- struct crypto_sync_skcipher *cbc_tfm,
- u32 offset, struct xdr_buf *buf, struct page **pages,
- u8 *iv, unsigned int ivsize)
-{
- u32 blocksize, nbytes, nblocks, cbcbytes;
- struct encryptor_desc desc;
- int err;
-
- blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
- nbytes = buf->len - offset;
- nblocks = (nbytes + blocksize - 1) / blocksize;
- cbcbytes = 0;
- if (nblocks > 2)
- cbcbytes = (nblocks - 2) * blocksize;
-
- memset(desc.iv, 0, sizeof(desc.iv));
-
- /* Handle block-sized chunks of plaintext with CBC. */
- if (cbcbytes) {
- SYNC_SKCIPHER_REQUEST_ON_STACK(req, cbc_tfm);
-
- desc.pos = offset;
- desc.fragno = 0;
- desc.fraglen = 0;
- desc.pages = pages;
- desc.outbuf = buf;
- desc.req = req;
-
- skcipher_request_set_sync_tfm(req, cbc_tfm);
- skcipher_request_set_callback(req, 0, NULL, NULL);
-
- sg_init_table(desc.infrags, 4);
- sg_init_table(desc.outfrags, 4);
-
- err = xdr_process_buf(buf, offset, cbcbytes, encryptor, &desc);
- skcipher_request_zero(req);
- if (err)
- return err;
- }
-
- /* Remaining plaintext is handled with CBC-CTS. */
- err = gss_krb5_cts_crypt(cts_tfm, buf, offset + cbcbytes,
- desc.iv, pages, 1);
- if (err)
- return err;
-
- if (unlikely(iv))
- memcpy(iv, desc.iv, ivsize);
- return 0;
-}
-EXPORT_SYMBOL_IF_KUNIT(krb5_cbc_cts_encrypt);
-
-/**
- * krb5_cbc_cts_decrypt - decrypt in CBC mode with CTS
- * @cts_tfm: CBC cipher with CTS
- * @cbc_tfm: base CBC cipher
- * @offset: starting byte offset for plaintext
- * @buf: OUT: output buffer
- *
- * Return values:
- * %0: decryption successful
- * negative errno: decryption could not be completed
+ * %GSS_S_COMPLETE: Encryption successful
+ * %GSS_S_FAILURE: Encryption failed
*/
-VISIBLE_IF_KUNIT
-int krb5_cbc_cts_decrypt(struct crypto_sync_skcipher *cts_tfm,
- struct crypto_sync_skcipher *cbc_tfm,
- u32 offset, struct xdr_buf *buf)
-{
- u32 blocksize, nblocks, cbcbytes;
- struct decryptor_desc desc;
- int err;
-
- blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
- nblocks = (buf->len + blocksize - 1) / blocksize;
- cbcbytes = 0;
- if (nblocks > 2)
- cbcbytes = (nblocks - 2) * blocksize;
-
- memset(desc.iv, 0, sizeof(desc.iv));
-
- /* Handle block-sized chunks of plaintext with CBC. */
- if (cbcbytes) {
- SYNC_SKCIPHER_REQUEST_ON_STACK(req, cbc_tfm);
-
- desc.fragno = 0;
- desc.fraglen = 0;
- desc.req = req;
-
- skcipher_request_set_sync_tfm(req, cbc_tfm);
- skcipher_request_set_callback(req, 0, NULL, NULL);
-
- sg_init_table(desc.frags, 4);
-
- err = xdr_process_buf(buf, 0, cbcbytes, decryptor, &desc);
- skcipher_request_zero(req);
- if (err)
- return err;
- }
-
- /* Remaining plaintext is handled with CBC-CTS. */
- return gss_krb5_cts_crypt(cts_tfm, buf, cbcbytes, desc.iv, NULL, 0);
-}
-EXPORT_SYMBOL_IF_KUNIT(krb5_cbc_cts_decrypt);
-
u32
-gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages)
+gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
+ struct xdr_buf *buf, struct page **pages)
{
- u32 err;
- struct xdr_netobj hmac;
- u8 *ecptr;
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- struct crypto_ahash *ahash;
- struct page **save_pages;
- unsigned int conflen;
-
- if (kctx->initiate) {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
- } else {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
- }
- conflen = crypto_sync_skcipher_blocksize(cipher);
+ const struct krb5_enctype *krb5 = kctx->krb5e;
+ struct crypto_aead *aead = kctx->initiate ?
+ kctx->initiator_enc_aead : kctx->acceptor_enc_aead;
+ unsigned int conflen = krb5->conf_len;
+ unsigned int cksum_len = krb5->cksum_len;
+ unsigned int sec_offset, sec_len, data_len;
+ struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow = NULL;
+ ssize_t ret;
+ int nsg;
- /* hide the gss token header and insert the confounder */
- offset += GSS_KRB5_TOK_HDR_LEN;
- if (xdr_extend_head(buf, offset, conflen))
+ /* Insert space for the confounder */
+ if (xdr_extend_head(buf, offset + GSS_KRB5_TOK_HDR_LEN, conflen))
return GSS_S_FAILURE;
- krb5_make_confounder(buf->head[0].iov_base + offset, conflen);
- offset -= GSS_KRB5_TOK_HDR_LEN;
- if (buf->tail[0].iov_base != NULL) {
- ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len;
- } else {
+ /* Ensure a tail segment exists */
+ if (buf->tail[0].iov_base == NULL) {
buf->tail[0].iov_base = buf->head[0].iov_base
- + buf->head[0].iov_len;
+ + buf->head[0].iov_len;
buf->tail[0].iov_len = 0;
- ecptr = buf->tail[0].iov_base;
}
- /* copy plaintext gss token header after filler (if any) */
- memcpy(ecptr, buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
+ /* Append a copy of the plaintext GSS token header (RFC 4121 Sec 4.2.4) */
+ memcpy(buf->tail[0].iov_base + buf->tail[0].iov_len,
+ buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
buf->len += GSS_KRB5_TOK_HDR_LEN;
- hmac.len = kctx->gk5e->cksumlength;
- hmac.data = buf->tail[0].iov_base + buf->tail[0].iov_len;
+ /* Reserve space for the integrity checksum */
+ buf->tail[0].iov_len += cksum_len;
+ buf->len += cksum_len;
/*
- * When we are called, pages points to the real page cache
- * data -- which we can't go and encrypt! buf->pages points
- * to scratch pages which we are going to send off to the
- * client/server. Swap in the plaintext pages to calculate
- * the hmac.
+ * The AEAD operates in-place, but on the client send path the
+ * plaintext payload lives in page cache pages that must not be
+ * modified. Copy the payload into the scratch output pages
+ * first. On the server send path @pages and buf->pages are
+ * the same array, and no copy is needed.
+ *
+ * Both arrays share buf->page_base, so the same index and
+ * intra-page offset address corresponding data in each.
*/
- save_pages = buf->pages;
- buf->pages = pages;
-
- err = gss_krb5_checksum(ahash, NULL, 0, buf,
- offset + GSS_KRB5_TOK_HDR_LEN, &hmac);
- buf->pages = save_pages;
- if (err)
- return GSS_S_FAILURE;
+ if (pages != buf->pages) {
+ unsigned int poff = buf->page_base;
+ unsigned int plen = buf->page_len;
+ unsigned int i = poff >> PAGE_SHIFT;
+ unsigned int off = offset_in_page(poff);
- err = krb5_cbc_cts_encrypt(cipher, aux_cipher,
- offset + GSS_KRB5_TOK_HDR_LEN,
- buf, pages, NULL, 0);
- if (err)
- return GSS_S_FAILURE;
-
- /* Now update buf to account for HMAC */
- buf->tail[0].iov_len += kctx->gk5e->cksumlength;
- buf->len += kctx->gk5e->cksumlength;
-
- return GSS_S_COMPLETE;
-}
-
-u32
-gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
-{
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- struct crypto_ahash *ahash;
- struct xdr_netobj our_hmac_obj;
- u8 our_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- u8 pkt_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- struct xdr_buf subbuf;
- u32 ret = 0;
-
- if (kctx->initiate) {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
- } else {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
+ while (plen) {
+ unsigned int n = min_t(unsigned int, plen,
+ PAGE_SIZE - off);
+ memcpy_page(buf->pages[i], off, pages[i], off, n);
+ plen -= n;
+ i++;
+ off = 0;
+ }
}
- /* create a segment skipping the header and leaving out the checksum */
- xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN,
- (len - offset - GSS_KRB5_TOK_HDR_LEN -
- kctx->gk5e->cksumlength));
-
- ret = krb5_cbc_cts_decrypt(cipher, aux_cipher, 0, &subbuf);
- if (ret)
- goto out_err;
-
- our_hmac_obj.len = kctx->gk5e->cksumlength;
- our_hmac_obj.data = our_hmac;
- ret = gss_krb5_checksum(ahash, NULL, 0, &subbuf, 0, &our_hmac_obj);
- if (ret)
- goto out_err;
+ /* Build scatterlist covering the secured region */
+ sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
+ sec_len = buf->len - sec_offset;
+ data_len = sec_len - conflen - cksum_len;
- /* Get the packet's hmac value */
- ret = read_bytes_from_xdr_buf(buf, len - kctx->gk5e->cksumlength,
- pkt_hmac, kctx->gk5e->cksumlength);
- if (ret)
- goto out_err;
-
- if (crypto_memneq(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) {
- ret = GSS_S_BAD_SIG;
- goto out_err;
- }
- *headskip = crypto_sync_skcipher_blocksize(cipher);
- *tailskip = kctx->gk5e->cksumlength;
-out_err:
- if (ret && ret != GSS_S_BAD_SIG)
- ret = GSS_S_FAILURE;
- return ret;
-}
-
-/**
- * krb5_etm_checksum - Compute a MAC for a GSS Wrap token
- * @cipher: an initialized cipher transform
- * @tfm: an initialized hash transform
- * @body: xdr_buf containing an RPC message (body.len is the message length)
- * @body_offset: byte offset into @body to start checksumming
- * @cksumout: OUT: a buffer to be filled in with the computed HMAC
- *
- * Usually expressed as H = HMAC(K, IV | ciphertext)[1..h] .
- *
- * Caller provides the truncation length of the output token (h) in
- * cksumout.len.
- *
- * Return values:
- * %GSS_S_COMPLETE: Digest computed, @cksumout filled in
- * %GSS_S_FAILURE: Call failed
- */
-VISIBLE_IF_KUNIT
-u32 krb5_etm_checksum(struct crypto_sync_skcipher *cipher,
- struct crypto_ahash *tfm, const struct xdr_buf *body,
- int body_offset, struct xdr_netobj *cksumout)
-{
- unsigned int ivsize = crypto_sync_skcipher_ivsize(cipher);
- struct ahash_request *req;
- struct scatterlist sg[1];
- u8 *iv, *checksumdata;
- int err = -ENOMEM;
-
- checksumdata = kmalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL);
- if (!checksumdata)
+ nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
+ sg, ARRAY_SIZE(sg),
+ &sg_overflow, GFP_NOFS);
+ if (nsg < 0)
return GSS_S_FAILURE;
- /* For RPCSEC, the "initial cipher state" is always all zeroes. */
- iv = kzalloc(ivsize, GFP_KERNEL);
- if (!iv)
- goto out_free_mem;
-
- req = ahash_request_alloc(tfm, GFP_KERNEL);
- if (!req)
- goto out_free_mem;
- ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
- err = crypto_ahash_init(req);
- if (err)
- goto out_free_ahash;
-
- sg_init_one(sg, iv, ivsize);
- ahash_request_set_crypt(req, sg, NULL, ivsize);
- err = crypto_ahash_update(req);
- if (err)
- goto out_free_ahash;
- err = xdr_process_buf(body, body_offset, body->len - body_offset,
- checksummer, req);
- if (err)
- goto out_free_ahash;
- ahash_request_set_crypt(req, NULL, checksumdata, 0);
- err = crypto_ahash_final(req);
- if (err)
- goto out_free_ahash;
- memcpy(cksumout->data, checksumdata, cksumout->len);
+ ret = crypto_krb5_encrypt(krb5, aead, sg, nsg, sec_len,
+ conflen, data_len, false);
+ kfree(sg_overflow);
+ if (ret < 0)
+ return GSS_S_FAILURE;
-out_free_ahash:
- ahash_request_free(req);
-out_free_mem:
- kfree(iv);
- kfree_sensitive(checksumdata);
- return err ? GSS_S_FAILURE : GSS_S_COMPLETE;
+ return GSS_S_COMPLETE;
}
-EXPORT_SYMBOL_IF_KUNIT(krb5_etm_checksum);
/**
- * krb5_etm_encrypt - Encrypt using the RFC 8009 rules
+ * gss_krb5_aead_decrypt - Decrypt a wrap token using crypto/krb5
* @kctx: Kerberos context
- * @offset: starting offset of the payload, in bytes
- * @buf: OUT: send buffer to contain the encrypted payload
- * @pages: plaintext payload
- *
- * The main difference with aes_encrypt is that "The HMAC is
- * calculated over the cipher state concatenated with the AES
- * output, instead of being calculated over the confounder and
- * plaintext. This allows the message receiver to verify the
- * integrity of the message before decrypting the message."
- *
- * RFC 8009 Section 5:
- *
- * encryption function: as follows, where E() is AES encryption in
- * CBC-CS3 mode, and h is the size of truncated HMAC (128 bits or
- * 192 bits as described above).
- *
- * N = random value of length 128 bits (the AES block size)
- * IV = cipher state
- * C = E(Ke, N | plaintext, IV)
- * H = HMAC(Ki, IV | C)
- * ciphertext = C | H[1..h]
- *
- * This encryption formula provides AEAD EtM with key separation.
+ * @offset: byte offset of the GSS token header in @buf
+ * @len: total length of the GSS token
+ * @buf: ciphertext buffer, decrypted in-place
+ * @headskip: OUT: confounder length, in octets
+ * @tailskip: OUT: checksum length, in octets
*
* Return values:
- * %GSS_S_COMPLETE: Encryption successful
- * %GSS_S_FAILURE: Encryption failed
+ * %GSS_S_COMPLETE: Decryption and integrity verification succeeded
+ * %GSS_S_BAD_SIG: Integrity checksum did not match
+ * %GSS_S_DEFECTIVE_TOKEN: Token is malformed or truncated
+ * %GSS_S_FAILURE: Decryption failed
*/
u32
-krb5_etm_encrypt(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages)
+gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
+ struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
{
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- struct crypto_ahash *ahash;
- struct xdr_netobj hmac;
- unsigned int conflen;
- u8 *ecptr;
- u32 err;
+ const struct krb5_enctype *krb5 = kctx->krb5e;
+ struct crypto_aead *aead = kctx->initiate ?
+ kctx->acceptor_enc_aead : kctx->initiator_enc_aead;
+ unsigned int sec_offset, sec_len;
+ size_t data_offset, data_len;
+ struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow = NULL;
+ int nsg, ret;
- if (kctx->initiate) {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
- } else {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
- }
- conflen = crypto_sync_skcipher_blocksize(cipher);
+ /* Secured region starts after the GSS token header */
+ sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
+ if (len < sec_offset)
+ return GSS_S_DEFECTIVE_TOKEN;
+ sec_len = len - sec_offset;
- offset += GSS_KRB5_TOK_HDR_LEN;
- if (xdr_extend_head(buf, offset, conflen))
+ nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
+ sg, ARRAY_SIZE(sg),
+ &sg_overflow, GFP_NOFS);
+ if (nsg < 0)
return GSS_S_FAILURE;
- krb5_make_confounder(buf->head[0].iov_base + offset, conflen);
- offset -= GSS_KRB5_TOK_HDR_LEN;
- if (buf->tail[0].iov_base) {
- ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len;
- } else {
- buf->tail[0].iov_base = buf->head[0].iov_base
- + buf->head[0].iov_len;
- buf->tail[0].iov_len = 0;
- ecptr = buf->tail[0].iov_base;
- }
-
- memcpy(ecptr, buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
- buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
- buf->len += GSS_KRB5_TOK_HDR_LEN;
-
- err = krb5_cbc_cts_encrypt(cipher, aux_cipher,
- offset + GSS_KRB5_TOK_HDR_LEN,
- buf, pages, NULL, 0);
- if (err)
- return GSS_S_FAILURE;
-
- hmac.data = buf->tail[0].iov_base + buf->tail[0].iov_len;
- hmac.len = kctx->gk5e->cksumlength;
- err = krb5_etm_checksum(cipher, ahash,
- buf, offset + GSS_KRB5_TOK_HDR_LEN, &hmac);
- if (err)
- goto out_err;
- buf->tail[0].iov_len += kctx->gk5e->cksumlength;
- buf->len += kctx->gk5e->cksumlength;
+ data_offset = 0;
+ data_len = sec_len;
+ ret = crypto_krb5_decrypt(krb5, aead, sg, nsg,
+ &data_offset, &data_len);
+ kfree(sg_overflow);
+ if (ret < 0)
+ return gss_krb5_errno_to_status(ret);
+ *headskip = data_offset;
+ *tailskip = sec_len - data_offset - data_len;
return GSS_S_COMPLETE;
-
-out_err:
- return GSS_S_FAILURE;
}
/**
- * krb5_etm_decrypt - Decrypt using the RFC 8009 rules
- * @kctx: Kerberos context
- * @offset: starting offset of the ciphertext, in bytes
- * @len: size of ciphertext to unwrap
- * @buf: ciphertext to unwrap
- * @headskip: OUT: the enctype's confounder length, in octets
- * @tailskip: OUT: the enctype's HMAC length, in octets
+ * gss_krb5_mic_build_sg - Build scatterlist for MIC token operations
+ * @body: xdr_buf containing the message body
+ * @cksum: pointer to checksum area in the token buffer
+ * @cksum_len: length of checksum area
+ * @hdr: pointer to GSS token header
+ * @sg_head: caller-provided scatterlist array; if more than
+ * XDR_BUF_TO_SG_NENTS entries are needed, an overflow
+ * scatterlist is allocated and chained automatically
+ * @sg_overflow: OUT: overflow scatterlist, caller must kfree
*
- * RFC 8009 Section 5:
+ * Per RFC 4121 Section 4.2.4, MIC token checksums cover the
+ * message body followed by the token header. The checksum
+ * output or received checksum occupies the first scatterlist
+ * entry. This layout cannot be constructed by
+ * xdr_buf_to_sg_alloc() because the checksum area and the GSS
+ * header lie outside the xdr_buf.
*
- * decryption function: as follows, where D() is AES decryption in
- * CBC-CS3 mode, and h is the size of truncated HMAC.
- *
- * (C, H) = ciphertext
- * (Note: H is the last h bits of the ciphertext.)
- * IV = cipher state
- * if H != HMAC(Ki, IV | C)[1..h]
- * stop, report error
- * (N, P) = D(Ke, C, IV)
- *
- * Return values:
- * %GSS_S_COMPLETE: Decryption successful
- * %GSS_S_BAD_SIG: computed HMAC != received HMAC
- * %GSS_S_FAILURE: Decryption failed
+ * Returns the number of scatterlist entries on success, or a
+ * negative errno on failure.
*/
-u32
-krb5_etm_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
+int gss_krb5_mic_build_sg(const struct xdr_buf *body,
+ void *cksum, unsigned int cksum_len,
+ void *hdr,
+ struct scatterlist *sg_head,
+ struct scatterlist **sg_overflow)
{
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- u8 our_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- u8 pkt_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- struct xdr_netobj our_hmac_obj;
- struct crypto_ahash *ahash;
- struct xdr_buf subbuf;
- u32 ret = 0;
+ struct scatterlist *entry;
+ int body_max, body_nsg, nsg;
- if (kctx->initiate) {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
+ *sg_overflow = NULL;
+
+ body_max = 2;
+ if (body->page_len)
+ body_max += DIV_ROUND_UP(body->page_len +
+ offset_in_page(body->page_base),
+ PAGE_SIZE);
+ nsg = 1 + body_max + 1;
+ if (nsg <= XDR_BUF_TO_SG_NENTS) {
+ sg_init_table(sg_head, nsg);
} else {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
- }
+ unsigned int overflow_nents =
+ nsg - XDR_BUF_TO_SG_NENTS + 1;
- /* Extract the ciphertext into @subbuf. */
- xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN,
- (len - offset - GSS_KRB5_TOK_HDR_LEN -
- kctx->gk5e->cksumlength));
+ *sg_overflow = kmalloc_array(overflow_nents,
+ sizeof(**sg_overflow),
+ GFP_NOFS);
+ if (!*sg_overflow)
+ return -ENOMEM;
- our_hmac_obj.data = our_hmac;
- our_hmac_obj.len = kctx->gk5e->cksumlength;
- ret = krb5_etm_checksum(cipher, ahash, &subbuf, 0, &our_hmac_obj);
- if (ret)
- goto out_err;
- ret = read_bytes_from_xdr_buf(buf, len - kctx->gk5e->cksumlength,
- pkt_hmac, kctx->gk5e->cksumlength);
- if (ret)
- goto out_err;
- if (crypto_memneq(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) {
- ret = GSS_S_BAD_SIG;
- goto out_err;
+ sg_init_table(sg_head, XDR_BUF_TO_SG_NENTS);
+ sg_init_table(*sg_overflow, overflow_nents);
+ sg_chain(sg_head, XDR_BUF_TO_SG_NENTS, *sg_overflow);
}
- ret = krb5_cbc_cts_decrypt(cipher, aux_cipher, 0, &subbuf);
- if (ret) {
- ret = GSS_S_FAILURE;
+ sg_set_buf(&sg_head[0], cksum, cksum_len);
+ body_nsg = xdr_buf_to_sg(body, 0, body->len,
+ sg_next(&sg_head[0]), body_max);
+ if (body_nsg < 0)
goto out_err;
- }
- *headskip = crypto_sync_skcipher_blocksize(cipher);
- *tailskip = kctx->gk5e->cksumlength;
- return GSS_S_COMPLETE;
+ /*
+ * xdr_buf_to_sg marks the last body entry as end-of-list;
+ * clear it so the trailing header entry is reachable.
+ */
+ if (body_nsg > 0) {
+ entry = sg_last(sg_next(&sg_head[0]), body_nsg);
+ sg_unmark_end(entry);
+ entry = sg_next(entry);
+ } else {
+ entry = sg_next(&sg_head[0]);
+ }
+ sg_set_buf(entry, hdr, GSS_KRB5_TOK_HDR_LEN);
+ sg_mark_end(entry);
+ return 1 + body_nsg + 1;
out_err:
- if (ret != GSS_S_BAD_SIG)
- ret = GSS_S_FAILURE;
- return ret;
+ kfree(*sg_overflow);
+ *sg_overflow = NULL;
+ return body_nsg;
}
diff --git a/net/sunrpc/auth_gss/gss_krb5_internal.h b/net/sunrpc/auth_gss/gss_krb5_internal.h
index 8769e9e705bf..3b392e96f25d 100644
--- a/net/sunrpc/auth_gss/gss_krb5_internal.h
+++ b/net/sunrpc/auth_gss/gss_krb5_internal.h
@@ -8,51 +8,10 @@
#ifndef _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H
#define _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H
-/*
- * The RFCs often specify payload lengths in bits. This helper
- * converts a specified bit-length to the number of octets/bytes.
- */
-#define BITS2OCTETS(x) ((x) / 8)
+#include <crypto/krb5.h>
struct krb5_ctx;
-struct gss_krb5_enctype {
- const u32 etype; /* encryption (key) type */
- const u32 ctype; /* checksum type */
- const char *name; /* "friendly" name */
- const char *encrypt_name; /* crypto encrypt name */
- const char *aux_cipher; /* aux encrypt cipher name */
- const char *cksum_name; /* crypto checksum name */
- const u16 signalg; /* signing algorithm */
- const u16 sealalg; /* sealing algorithm */
- const u32 cksumlength; /* checksum length */
- const u32 keyed_cksum; /* is it a keyed cksum? */
- const u32 keybytes; /* raw key len, in bytes */
- const u32 keylength; /* protocol key length, in octets */
- const u32 Kc_length; /* checksum subkey length, in octets */
- const u32 Ke_length; /* encryption subkey length, in octets */
- const u32 Ki_length; /* integrity subkey length, in octets */
-
- int (*derive_key)(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *in,
- struct xdr_netobj *out,
- const struct xdr_netobj *label,
- gfp_t gfp_mask);
- u32 (*encrypt)(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages);
- u32 (*decrypt)(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
- u32 (*get_mic)(struct krb5_ctx *kctx, struct xdr_buf *text,
- struct xdr_netobj *token);
- u32 (*verify_mic)(struct krb5_ctx *kctx, struct xdr_buf *message_buffer,
- struct xdr_netobj *read_token);
- u32 (*wrap)(struct krb5_ctx *kctx, int offset,
- struct xdr_buf *buf, struct page **pages);
- u32 (*unwrap)(struct krb5_ctx *kctx, int offset, int len,
- struct xdr_buf *buf, unsigned int *slack,
- unsigned int *align);
-};
-
/* krb5_ctx flags definitions */
#define KRB5_CTX_FLAG_INITIATOR 0x00000001
#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
@@ -61,20 +20,12 @@ struct krb5_ctx {
int initiate; /* 1 = initiating, 0 = accepting */
u32 enctype;
u32 flags;
- const struct gss_krb5_enctype *gk5e; /* enctype-specific info */
- struct crypto_sync_skcipher *enc;
- struct crypto_sync_skcipher *seq;
- struct crypto_sync_skcipher *acceptor_enc;
- struct crypto_sync_skcipher *initiator_enc;
- struct crypto_sync_skcipher *acceptor_enc_aux;
- struct crypto_sync_skcipher *initiator_enc_aux;
- struct crypto_ahash *acceptor_sign;
- struct crypto_ahash *initiator_sign;
- struct crypto_ahash *initiator_integ;
- struct crypto_ahash *acceptor_integ;
+ const struct krb5_enctype *krb5e; /* crypto/krb5 enctype */
+ struct crypto_aead *initiator_enc_aead;
+ struct crypto_aead *acceptor_enc_aead;
+ struct crypto_shash *initiator_sign_shash;
+ struct crypto_shash *acceptor_sign_shash;
u8 Ksess[GSS_KRB5_MAX_KEYLEN]; /* session key */
- u8 cksum[GSS_KRB5_MAX_KEYLEN];
- atomic_t seq_send;
atomic64_t seq_send64;
time64_t endtime;
struct xdr_netobj mech_used;
@@ -101,95 +52,21 @@ u32 gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
* Implementation internal functions
*/
-/* Key Derivation Functions */
-
-int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- const struct xdr_netobj *label,
- gfp_t gfp_mask);
-
-int krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- const struct xdr_netobj *in_constant,
- gfp_t gfp_mask);
-
-int krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- const struct xdr_netobj *in_constant,
- gfp_t gfp_mask);
-
-/**
- * krb5_derive_key - Derive a subkey from a protocol key
- * @kctx: Kerberos 5 context
- * @inkey: base protocol key
- * @outkey: OUT: derived key
- * @usage: key usage value
- * @seed: key usage seed (one octet)
- * @gfp_mask: memory allocation control flags
- *
- * Caller sets @outkey->len to the desired length of the derived key.
- *
- * On success, returns 0 and fills in @outkey. A negative errno value
- * is returned on failure.
- */
-static inline int krb5_derive_key(struct krb5_ctx *kctx,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- u32 usage, u8 seed, gfp_t gfp_mask)
-{
- const struct gss_krb5_enctype *gk5e = kctx->gk5e;
- u8 label_data[GSS_KRB5_K5CLENGTH];
- struct xdr_netobj label = {
- .len = sizeof(label_data),
- .data = label_data,
- };
- __be32 *p = (__be32 *)label_data;
-
- *p = cpu_to_be32(usage);
- label_data[4] = seed;
- return gk5e->derive_key(gk5e, inkey, outkey, &label, gfp_mask);
-}
-
-void krb5_make_confounder(u8 *p, int conflen);
-
-u32 gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
- const struct xdr_buf *body, int body_offset,
- struct xdr_netobj *cksumout);
-
-u32 krb5_encrypt(struct crypto_sync_skcipher *key, void *iv, void *in,
- void *out, int length);
-
int xdr_extend_head(struct xdr_buf *buf, unsigned int base,
unsigned int shiftlen);
-u32 gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages);
-
-u32 gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *plainoffset, u32 *plainlen);
+u32 gss_krb5_errno_to_status(int err);
-u32 krb5_etm_encrypt(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf,
- struct page **pages);
+int gss_krb5_mic_build_sg(const struct xdr_buf *body,
+ void *cksum, unsigned int cksum_len,
+ void *hdr,
+ struct scatterlist *sg_head,
+ struct scatterlist **sg_overflow);
-u32 krb5_etm_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
+u32 gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
+ struct xdr_buf *buf, struct page **pages);
+u32 gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
+ struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
-#if IS_ENABLED(CONFIG_KUNIT)
-void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out);
-const struct gss_krb5_enctype *gss_krb5_lookup_enctype(u32 etype);
-int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm,
- struct crypto_sync_skcipher *cbc_tfm, u32 offset,
- struct xdr_buf *buf, struct page **pages,
- u8 *iv, unsigned int ivsize);
-int krb5_cbc_cts_decrypt(struct crypto_sync_skcipher *cts_tfm,
- struct crypto_sync_skcipher *cbc_tfm,
- u32 offset, struct xdr_buf *buf);
-u32 krb5_etm_checksum(struct crypto_sync_skcipher *cipher,
- struct crypto_ahash *tfm, const struct xdr_buf *body,
- int body_offset, struct xdr_netobj *cksumout);
-#endif
#endif /* _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H */
diff --git a/net/sunrpc/auth_gss/gss_krb5_keys.c b/net/sunrpc/auth_gss/gss_krb5_keys.c
deleted file mode 100644
index 4eb19c3a54c7..000000000000
--- a/net/sunrpc/auth_gss/gss_krb5_keys.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * COPYRIGHT (c) 2008
- * The Regents of the University of Michigan
- * ALL RIGHTS RESERVED
- *
- * Permission is granted to use, copy, create derivative works
- * and redistribute this software and such derivative works
- * for any purpose, so long as the name of The University of
- * Michigan is not used in any advertising or publicity
- * pertaining to the use of distribution of this software
- * without specific, written prior authorization. If the
- * above copyright notice or any other identification of the
- * University of Michigan is included in any copy of any
- * portion of this software, then the disclaimer below must
- * also be included.
- *
- * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
- * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
- * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
- * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
- * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
- * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
- * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
- * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
- * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGES.
- */
-
-/*
- * Copyright (C) 1998 by the FundsXpress, INC.
- *
- * All rights reserved.
- *
- * Export of this software from the United States of America may require
- * a specific license from the United States Government. It is the
- * responsibility of any person or organization contemplating export to
- * obtain such a license before exporting.
- *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of FundsXpress. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission. FundsXpress makes no representations about the suitability of
- * this software for any purpose. It is provided "as is" without express
- * or implied warranty.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-#include <crypto/skcipher.h>
-#include <linux/err.h>
-#include <linux/types.h>
-#include <linux/sunrpc/gss_krb5.h>
-#include <linux/sunrpc/xdr.h>
-#include <linux/lcm.h>
-#include <crypto/hash.h>
-#include <kunit/visibility.h>
-
-#include "gss_krb5_internal.h"
-
-#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
-# define RPCDBG_FACILITY RPCDBG_AUTH
-#endif
-
-/**
- * krb5_nfold - n-fold function
- * @inbits: number of bits in @in
- * @in: buffer containing input to fold
- * @outbits: number of bits in the output buffer
- * @out: buffer to hold the result
- *
- * This is the n-fold function as described in rfc3961, sec 5.1
- * Taken from MIT Kerberos and modified.
- */
-VISIBLE_IF_KUNIT
-void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out)
-{
- unsigned long ulcm;
- int byte, i, msbit;
-
- /* the code below is more readable if I make these bytes
- instead of bits */
-
- inbits >>= 3;
- outbits >>= 3;
-
- /* first compute lcm(n,k) */
- ulcm = lcm(inbits, outbits);
-
- /* now do the real work */
-
- memset(out, 0, outbits);
- byte = 0;
-
- /* this will end up cycling through k lcm(k,n)/k times, which
- is correct */
- for (i = ulcm-1; i >= 0; i--) {
- /* compute the msbit in k which gets added into this byte */
- msbit = (
- /* first, start with the msbit in the first,
- * unrotated byte */
- ((inbits << 3) - 1)
- /* then, for each byte, shift to the right
- * for each repetition */
- + (((inbits << 3) + 13) * (i/inbits))
- /* last, pick out the correct byte within
- * that shifted repetition */
- + ((inbits - (i % inbits)) << 3)
- ) % (inbits << 3);
-
- /* pull out the byte value itself */
- byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8)|
- (in[((inbits) - (msbit >> 3)) % inbits]))
- >> ((msbit & 7) + 1)) & 0xff;
-
- /* do the addition */
- byte += out[i % outbits];
- out[i % outbits] = byte & 0xff;
-
- /* keep around the carry bit, if any */
- byte >>= 8;
-
- }
-
- /* if there's a carry bit left over, add it back in */
- if (byte) {
- for (i = outbits - 1; i >= 0; i--) {
- /* do the addition */
- byte += out[i];
- out[i] = byte & 0xff;
-
- /* keep around the carry bit, if any */
- byte >>= 8;
- }
- }
-}
-EXPORT_SYMBOL_IF_KUNIT(krb5_nfold);
-
-/*
- * This is the DK (derive_key) function as described in rfc3961, sec 5.1
- * Taken from MIT Kerberos and modified.
- */
-static int krb5_DK(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *inkey, u8 *rawkey,
- const struct xdr_netobj *in_constant, gfp_t gfp_mask)
-{
- size_t blocksize, keybytes, keylength, n;
- unsigned char *inblockdata, *outblockdata;
- struct xdr_netobj inblock, outblock;
- struct crypto_sync_skcipher *cipher;
- int ret = -EINVAL;
-
- keybytes = gk5e->keybytes;
- keylength = gk5e->keylength;
-
- if (inkey->len != keylength)
- goto err_return;
-
- cipher = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- if (IS_ERR(cipher))
- goto err_return;
- blocksize = crypto_sync_skcipher_blocksize(cipher);
- if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len))
- goto err_free_cipher;
-
- ret = -ENOMEM;
- inblockdata = kmalloc(blocksize, gfp_mask);
- if (inblockdata == NULL)
- goto err_free_cipher;
-
- outblockdata = kmalloc(blocksize, gfp_mask);
- if (outblockdata == NULL)
- goto err_free_in;
-
- inblock.data = (char *) inblockdata;
- inblock.len = blocksize;
-
- outblock.data = (char *) outblockdata;
- outblock.len = blocksize;
-
- /* initialize the input block */
-
- if (in_constant->len == inblock.len) {
- memcpy(inblock.data, in_constant->data, inblock.len);
- } else {
- krb5_nfold(in_constant->len * 8, in_constant->data,
- inblock.len * 8, inblock.data);
- }
-
- /* loop encrypting the blocks until enough key bytes are generated */
-
- n = 0;
- while (n < keybytes) {
- krb5_encrypt(cipher, NULL, inblock.data, outblock.data,
- inblock.len);
-
- if ((keybytes - n) <= outblock.len) {
- memcpy(rawkey + n, outblock.data, (keybytes - n));
- break;
- }
-
- memcpy(rawkey + n, outblock.data, outblock.len);
- memcpy(inblock.data, outblock.data, outblock.len);
- n += outblock.len;
- }
-
- ret = 0;
-
- kfree_sensitive(outblockdata);
-err_free_in:
- kfree_sensitive(inblockdata);
-err_free_cipher:
- crypto_free_sync_skcipher(cipher);
-err_return:
- return ret;
-}
-
-/*
- * This is the identity function, with some sanity checking.
- */
-static int krb5_random_to_key_v2(const struct gss_krb5_enctype *gk5e,
- struct xdr_netobj *randombits,
- struct xdr_netobj *key)
-{
- int ret = -EINVAL;
-
- if (key->len != 16 && key->len != 32) {
- dprintk("%s: key->len is %d\n", __func__, key->len);
- goto err_out;
- }
- if (randombits->len != 16 && randombits->len != 32) {
- dprintk("%s: randombits->len is %d\n",
- __func__, randombits->len);
- goto err_out;
- }
- if (randombits->len != key->len) {
- dprintk("%s: randombits->len is %d, key->len is %d\n",
- __func__, randombits->len, key->len);
- goto err_out;
- }
- memcpy(key->data, randombits->data, key->len);
- ret = 0;
-err_out:
- return ret;
-}
-
-/**
- * krb5_derive_key_v2 - Derive a subkey for an RFC 3962 enctype
- * @gk5e: Kerberos 5 enctype profile
- * @inkey: base protocol key
- * @outkey: OUT: derived key
- * @label: subkey usage label
- * @gfp_mask: memory allocation control flags
- *
- * Caller sets @outkey->len to the desired length of the derived key.
- *
- * On success, returns 0 and fills in @outkey. A negative errno value
- * is returned on failure.
- */
-int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- const struct xdr_netobj *label,
- gfp_t gfp_mask)
-{
- struct xdr_netobj inblock;
- int ret;
-
- inblock.len = gk5e->keybytes;
- inblock.data = kmalloc(inblock.len, gfp_mask);
- if (!inblock.data)
- return -ENOMEM;
-
- ret = krb5_DK(gk5e, inkey, inblock.data, label, gfp_mask);
- if (!ret)
- ret = krb5_random_to_key_v2(gk5e, &inblock, outkey);
-
- kfree_sensitive(inblock.data);
- return ret;
-}
-
-/*
- * K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
- *
- * i: A block counter is used with a length of 4 bytes, represented
- * in big-endian order.
- *
- * constant: The label input to the KDF is the usage constant supplied
- * to the key derivation function
- *
- * k: The length of the output key in bits, represented as a 4-byte
- * string in big-endian order.
- *
- * Caller fills in K(i-1) in @step, and receives the result K(i)
- * in the same buffer.
- */
-static int
-krb5_cmac_Ki(struct crypto_shash *tfm, const struct xdr_netobj *constant,
- u32 outlen, u32 count, struct xdr_netobj *step)
-{
- __be32 k = cpu_to_be32(outlen * 8);
- SHASH_DESC_ON_STACK(desc, tfm);
- __be32 i = cpu_to_be32(count);
- u8 zero = 0;
- int ret;
-
- desc->tfm = tfm;
- ret = crypto_shash_init(desc);
- if (ret)
- goto out_err;
-
- ret = crypto_shash_update(desc, step->data, step->len);
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, (u8 *)&i, sizeof(i));
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, constant->data, constant->len);
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, &zero, sizeof(zero));
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, (u8 *)&k, sizeof(k));
- if (ret)
- goto out_err;
- ret = crypto_shash_final(desc, step->data);
- if (ret)
- goto out_err;
-
-out_err:
- shash_desc_zero(desc);
- return ret;
-}
-
-/**
- * krb5_kdf_feedback_cmac - Derive a subkey for a Camellia/CMAC-based enctype
- * @gk5e: Kerberos 5 enctype parameters
- * @inkey: base protocol key
- * @outkey: OUT: derived key
- * @constant: subkey usage label
- * @gfp_mask: memory allocation control flags
- *
- * RFC 6803 Section 3:
- *
- * "We use a key derivation function from the family specified in
- * [SP800-108], Section 5.2, 'KDF in Feedback Mode'."
- *
- * n = ceiling(k / 128)
- * K(0) = zeros
- * K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
- * DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n))
- * KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant))
- *
- * Caller sets @outkey->len to the desired length of the derived key (k).
- *
- * On success, returns 0 and fills in @outkey. A negative errno value
- * is returned on failure.
- */
-int
-krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- const struct xdr_netobj *constant,
- gfp_t gfp_mask)
-{
- struct xdr_netobj step = { .data = NULL };
- struct xdr_netobj DR = { .data = NULL };
- unsigned int blocksize, offset;
- struct crypto_shash *tfm;
- int n, count, ret;
-
- /*
- * This implementation assumes the CMAC used for an enctype's
- * key derivation is the same as the CMAC used for its
- * checksumming. This happens to be true for enctypes that
- * are currently supported by this implementation.
- */
- tfm = crypto_alloc_shash(gk5e->cksum_name, 0, 0);
- if (IS_ERR(tfm)) {
- ret = PTR_ERR(tfm);
- goto out;
- }
- ret = crypto_shash_setkey(tfm, inkey->data, inkey->len);
- if (ret)
- goto out_free_tfm;
-
- blocksize = crypto_shash_digestsize(tfm);
- n = (outkey->len + blocksize - 1) / blocksize;
-
- /* K(0) is all zeroes */
- ret = -ENOMEM;
- step.len = blocksize;
- step.data = kzalloc(step.len, gfp_mask);
- if (!step.data)
- goto out_free_tfm;
-
- DR.len = blocksize * n;
- DR.data = kmalloc(DR.len, gfp_mask);
- if (!DR.data)
- goto out_free_tfm;
-
- /* XXX: Does not handle partial-block key sizes */
- for (offset = 0, count = 1; count <= n; count++) {
- ret = krb5_cmac_Ki(tfm, constant, outkey->len, count, &step);
- if (ret)
- goto out_free_tfm;
-
- memcpy(DR.data + offset, step.data, blocksize);
- offset += blocksize;
- }
-
- /* k-truncate and random-to-key */
- memcpy(outkey->data, DR.data, outkey->len);
- ret = 0;
-
-out_free_tfm:
- crypto_free_shash(tfm);
-out:
- kfree_sensitive(step.data);
- kfree_sensitive(DR.data);
- return ret;
-}
-
-/*
- * K1 = HMAC-SHA(key, 0x00000001 | label | 0x00 | k)
- *
- * key: The source of entropy from which subsequent keys are derived.
- *
- * label: An octet string describing the intended usage of the
- * derived key.
- *
- * k: Length in bits of the key to be outputted, expressed in
- * big-endian binary representation in 4 bytes.
- */
-static int
-krb5_hmac_K1(struct crypto_shash *tfm, const struct xdr_netobj *label,
- u32 outlen, struct xdr_netobj *K1)
-{
- __be32 k = cpu_to_be32(outlen * 8);
- SHASH_DESC_ON_STACK(desc, tfm);
- __be32 one = cpu_to_be32(1);
- u8 zero = 0;
- int ret;
-
- desc->tfm = tfm;
- ret = crypto_shash_init(desc);
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, (u8 *)&one, sizeof(one));
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, label->data, label->len);
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, &zero, sizeof(zero));
- if (ret)
- goto out_err;
- ret = crypto_shash_update(desc, (u8 *)&k, sizeof(k));
- if (ret)
- goto out_err;
- ret = crypto_shash_final(desc, K1->data);
- if (ret)
- goto out_err;
-
-out_err:
- shash_desc_zero(desc);
- return ret;
-}
-
-/**
- * krb5_kdf_hmac_sha2 - Derive a subkey for an AES/SHA2-based enctype
- * @gk5e: Kerberos 5 enctype policy parameters
- * @inkey: base protocol key
- * @outkey: OUT: derived key
- * @label: subkey usage label
- * @gfp_mask: memory allocation control flags
- *
- * RFC 8009 Section 3:
- *
- * "We use a key derivation function from Section 5.1 of [SP800-108],
- * which uses the HMAC algorithm as the PRF."
- *
- * function KDF-HMAC-SHA2(key, label, [context,] k):
- * k-truncate(K1)
- *
- * Caller sets @outkey->len to the desired length of the derived key.
- *
- * On success, returns 0 and fills in @outkey. A negative errno value
- * is returned on failure.
- */
-int
-krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- const struct xdr_netobj *label,
- gfp_t gfp_mask)
-{
- struct crypto_shash *tfm;
- struct xdr_netobj K1 = {
- .data = NULL,
- };
- int ret;
-
- /*
- * This implementation assumes the HMAC used for an enctype's
- * key derivation is the same as the HMAC used for its
- * checksumming. This happens to be true for enctypes that
- * are currently supported by this implementation.
- */
- tfm = crypto_alloc_shash(gk5e->cksum_name, 0, 0);
- if (IS_ERR(tfm)) {
- ret = PTR_ERR(tfm);
- goto out;
- }
- ret = crypto_shash_setkey(tfm, inkey->data, inkey->len);
- if (ret)
- goto out_free_tfm;
-
- K1.len = crypto_shash_digestsize(tfm);
- K1.data = kmalloc(K1.len, gfp_mask);
- if (!K1.data) {
- ret = -ENOMEM;
- goto out_free_tfm;
- }
-
- ret = krb5_hmac_K1(tfm, label, outkey->len, &K1);
- if (ret)
- goto out_free_tfm;
-
- /* k-truncate and random-to-key */
- memcpy(outkey->data, K1.data, outkey->len);
-
-out_free_tfm:
- kfree_sensitive(K1.data);
- crypto_free_shash(tfm);
-out:
- return ret;
-}
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c
index 6db64a9111a9..c41b5f3e1789 100644
--- a/net/sunrpc/auth_gss/gss_krb5_mech.c
+++ b/net/sunrpc/auth_gss/gss_krb5_mech.c
@@ -9,8 +9,6 @@
* J. Bruce Fields <bfields@umich.edu>
*/
-#include <crypto/hash.h>
-#include <crypto/skcipher.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -19,7 +17,6 @@
#include <linux/sunrpc/auth.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
-#include <kunit/visibility.h>
#include "auth_gss_internal.h"
#include "gss_krb5_internal.h"
@@ -30,201 +27,24 @@
static struct gss_api_mech gss_kerberos_mech;
-static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1)
- /*
- * AES-128 with SHA-1 (RFC 3962)
- */
- {
- .etype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .ctype = CKSUMTYPE_HMAC_SHA1_96_AES128,
- .name = "aes128-cts",
- .encrypt_name = "cts(cbc(aes))",
- .aux_cipher = "cbc(aes)",
- .cksum_name = "hmac(sha1)",
- .derive_key = krb5_derive_key_v2,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
-
- .signalg = -1,
- .sealalg = -1,
- .keybytes = 16,
- .keylength = BITS2OCTETS(128),
- .Kc_length = BITS2OCTETS(128),
- .Ke_length = BITS2OCTETS(128),
- .Ki_length = BITS2OCTETS(128),
- .cksumlength = BITS2OCTETS(96),
- .keyed_cksum = 1,
- },
- /*
- * AES-256 with SHA-1 (RFC 3962)
- */
- {
- .etype = ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- .ctype = CKSUMTYPE_HMAC_SHA1_96_AES256,
- .name = "aes256-cts",
- .encrypt_name = "cts(cbc(aes))",
- .aux_cipher = "cbc(aes)",
- .cksum_name = "hmac(sha1)",
- .derive_key = krb5_derive_key_v2,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
-
- .signalg = -1,
- .sealalg = -1,
- .keybytes = 32,
- .keylength = BITS2OCTETS(256),
- .Kc_length = BITS2OCTETS(256),
- .Ke_length = BITS2OCTETS(256),
- .Ki_length = BITS2OCTETS(256),
- .cksumlength = BITS2OCTETS(96),
- .keyed_cksum = 1,
- },
-#endif
-
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA)
- /*
- * Camellia-128 with CMAC (RFC 6803)
- */
- {
- .etype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .ctype = CKSUMTYPE_CMAC_CAMELLIA128,
- .name = "camellia128-cts-cmac",
- .encrypt_name = "cts(cbc(camellia))",
- .aux_cipher = "cbc(camellia)",
- .cksum_name = "cmac(camellia)",
- .cksumlength = BITS2OCTETS(128),
- .keyed_cksum = 1,
- .keylength = BITS2OCTETS(128),
- .Kc_length = BITS2OCTETS(128),
- .Ke_length = BITS2OCTETS(128),
- .Ki_length = BITS2OCTETS(128),
-
- .derive_key = krb5_kdf_feedback_cmac,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
- /*
- * Camellia-256 with CMAC (RFC 6803)
- */
- {
- .etype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .ctype = CKSUMTYPE_CMAC_CAMELLIA256,
- .name = "camellia256-cts-cmac",
- .encrypt_name = "cts(cbc(camellia))",
- .aux_cipher = "cbc(camellia)",
- .cksum_name = "cmac(camellia)",
- .cksumlength = BITS2OCTETS(128),
- .keyed_cksum = 1,
- .keylength = BITS2OCTETS(256),
- .Kc_length = BITS2OCTETS(256),
- .Ke_length = BITS2OCTETS(256),
- .Ki_length = BITS2OCTETS(256),
-
- .derive_key = krb5_kdf_feedback_cmac,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
-#endif
-
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2)
- /*
- * AES-128 with SHA-256 (RFC 8009)
- */
- {
- .etype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .ctype = CKSUMTYPE_HMAC_SHA256_128_AES128,
- .name = "aes128-cts-hmac-sha256-128",
- .encrypt_name = "cts(cbc(aes))",
- .aux_cipher = "cbc(aes)",
- .cksum_name = "hmac(sha256)",
- .cksumlength = BITS2OCTETS(128),
- .keyed_cksum = 1,
- .keylength = BITS2OCTETS(128),
- .Kc_length = BITS2OCTETS(128),
- .Ke_length = BITS2OCTETS(128),
- .Ki_length = BITS2OCTETS(128),
-
- .derive_key = krb5_kdf_hmac_sha2,
- .encrypt = krb5_etm_encrypt,
- .decrypt = krb5_etm_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
- /*
- * AES-256 with SHA-384 (RFC 8009)
- */
- {
- .etype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .ctype = CKSUMTYPE_HMAC_SHA384_192_AES256,
- .name = "aes256-cts-hmac-sha384-192",
- .encrypt_name = "cts(cbc(aes))",
- .aux_cipher = "cbc(aes)",
- .cksum_name = "hmac(sha384)",
- .cksumlength = BITS2OCTETS(192),
- .keyed_cksum = 1,
- .keylength = BITS2OCTETS(256),
- .Kc_length = BITS2OCTETS(192),
- .Ke_length = BITS2OCTETS(256),
- .Ki_length = BITS2OCTETS(192),
-
- .derive_key = krb5_kdf_hmac_sha2,
- .encrypt = krb5_etm_encrypt,
- .decrypt = krb5_etm_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
-#endif
-};
-
/*
- * The list of advertised enctypes is specified in order of most
- * preferred to least.
+ * Candidate enctypes in order of most preferred to least.
+ * Each is probed against crypto/krb5 at module init; only
+ * enctypes that crypto/krb5 supports are advertised.
*/
+static const u32 gss_krb5_enctypes[] = {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+ KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+};
+
static char gss_krb5_enctype_priority_list[64];
static void gss_krb5_prepare_enctype_priority_list(void)
{
- static const u32 gss_krb5_enctypes[] = {
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2)
- ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- ENCTYPE_AES128_CTS_HMAC_SHA256_128,
-#endif
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA)
- ENCTYPE_CAMELLIA256_CTS_CMAC,
- ENCTYPE_CAMELLIA128_CTS_CMAC,
-#endif
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1)
- ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- ENCTYPE_AES128_CTS_HMAC_SHA1_96,
-#endif
- };
size_t total, i;
char buf[16];
char *sep;
@@ -233,6 +53,8 @@ static void gss_krb5_prepare_enctype_priority_list(void)
sep = "";
gss_krb5_enctype_priority_list[0] = '\0';
for (total = 0, i = 0; i < ARRAY_SIZE(gss_krb5_enctypes); i++) {
+ if (!crypto_krb5_find_enctype(gss_krb5_enctypes[i]))
+ continue;
n = sprintf(buf, "%s%u", sep, gss_krb5_enctypes[i]);
if (n < 0)
break;
@@ -244,151 +66,56 @@ static void gss_krb5_prepare_enctype_priority_list(void)
}
}
-/**
- * gss_krb5_lookup_enctype - Retrieve profile information for a given enctype
- * @etype: ENCTYPE value
- *
- * Returns a pointer to a gss_krb5_enctype structure, or NULL if no
- * matching etype is found.
- */
-VISIBLE_IF_KUNIT
-const struct gss_krb5_enctype *gss_krb5_lookup_enctype(u32 etype)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(supported_gss_krb5_enctypes); i++)
- if (supported_gss_krb5_enctypes[i].etype == etype)
- return &supported_gss_krb5_enctypes[i];
- return NULL;
-}
-EXPORT_SYMBOL_IF_KUNIT(gss_krb5_lookup_enctype);
-
-static struct crypto_sync_skcipher *
-gss_krb5_alloc_cipher_v2(const char *cname, const struct xdr_netobj *key)
-{
- struct crypto_sync_skcipher *tfm;
-
- tfm = crypto_alloc_sync_skcipher(cname, 0, 0);
- if (IS_ERR(tfm))
- return NULL;
- if (crypto_sync_skcipher_setkey(tfm, key->data, key->len)) {
- crypto_free_sync_skcipher(tfm);
- return NULL;
- }
- return tfm;
-}
-
-static struct crypto_ahash *
-gss_krb5_alloc_hash_v2(struct krb5_ctx *kctx, const struct xdr_netobj *key)
-{
- struct crypto_ahash *tfm;
-
- tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm))
- return NULL;
- if (crypto_ahash_setkey(tfm, key->data, key->len)) {
- crypto_free_ahash(tfm);
- return NULL;
- }
- return tfm;
-}
-
static int
gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
{
- struct xdr_netobj keyin = {
- .len = ctx->gk5e->keylength,
+ struct krb5_buffer TK = {
+ .len = ctx->krb5e->key_len,
.data = ctx->Ksess,
};
- struct xdr_netobj keyout;
- int ret = -EINVAL;
-
- keyout.data = kmalloc(GSS_KRB5_MAX_KEYLEN, gfp_mask);
- if (!keyout.data)
- return -ENOMEM;
-
- /* initiator seal encryption */
- keyout.len = ctx->gk5e->Ke_length;
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL,
- KEY_USAGE_SEED_ENCRYPTION, gfp_mask))
- goto out;
- ctx->initiator_enc = gss_krb5_alloc_cipher_v2(ctx->gk5e->encrypt_name,
- &keyout);
- if (ctx->initiator_enc == NULL)
- goto out;
- if (ctx->gk5e->aux_cipher) {
- ctx->initiator_enc_aux =
- gss_krb5_alloc_cipher_v2(ctx->gk5e->aux_cipher,
- &keyout);
- if (ctx->initiator_enc_aux == NULL)
- goto out_free;
- }
+ int ret;
- /* acceptor seal encryption */
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
- KEY_USAGE_SEED_ENCRYPTION, gfp_mask))
+ ctx->initiator_enc_aead =
+ crypto_krb5_prepare_encryption(ctx->krb5e, &TK,
+ KG_USAGE_INITIATOR_SEAL,
+ gfp_mask);
+ if (IS_ERR(ctx->initiator_enc_aead)) {
+ ret = PTR_ERR(ctx->initiator_enc_aead);
goto out_free;
- ctx->acceptor_enc = gss_krb5_alloc_cipher_v2(ctx->gk5e->encrypt_name,
- &keyout);
- if (ctx->acceptor_enc == NULL)
- goto out_free;
- if (ctx->gk5e->aux_cipher) {
- ctx->acceptor_enc_aux =
- gss_krb5_alloc_cipher_v2(ctx->gk5e->aux_cipher,
- &keyout);
- if (ctx->acceptor_enc_aux == NULL)
- goto out_free;
}
-
- /* initiator sign checksum */
- keyout.len = ctx->gk5e->Kc_length;
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SIGN,
- KEY_USAGE_SEED_CHECKSUM, gfp_mask))
+ ctx->acceptor_enc_aead =
+ crypto_krb5_prepare_encryption(ctx->krb5e, &TK,
+ KG_USAGE_ACCEPTOR_SEAL,
+ gfp_mask);
+ if (IS_ERR(ctx->acceptor_enc_aead)) {
+ ret = PTR_ERR(ctx->acceptor_enc_aead);
goto out_free;
- ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->initiator_sign == NULL)
- goto out_free;
-
- /* acceptor sign checksum */
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SIGN,
- KEY_USAGE_SEED_CHECKSUM, gfp_mask))
- goto out_free;
- ctx->acceptor_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->acceptor_sign == NULL)
- goto out_free;
-
- /* initiator seal integrity */
- keyout.len = ctx->gk5e->Ki_length;
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL,
- KEY_USAGE_SEED_INTEGRITY, gfp_mask))
- goto out_free;
- ctx->initiator_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->initiator_integ == NULL)
- goto out_free;
-
- /* acceptor seal integrity */
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
- KEY_USAGE_SEED_INTEGRITY, gfp_mask))
+ }
+ ctx->initiator_sign_shash =
+ crypto_krb5_prepare_checksum(ctx->krb5e, &TK,
+ KG_USAGE_INITIATOR_SIGN,
+ gfp_mask);
+ if (IS_ERR(ctx->initiator_sign_shash)) {
+ ret = PTR_ERR(ctx->initiator_sign_shash);
goto out_free;
- ctx->acceptor_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->acceptor_integ == NULL)
+ }
+ ctx->acceptor_sign_shash =
+ crypto_krb5_prepare_checksum(ctx->krb5e, &TK,
+ KG_USAGE_ACCEPTOR_SIGN,
+ gfp_mask);
+ if (IS_ERR(ctx->acceptor_sign_shash)) {
+ ret = PTR_ERR(ctx->acceptor_sign_shash);
goto out_free;
+ }
- ret = 0;
-out:
- kfree_sensitive(keyout.data);
- return ret;
+ return 0;
out_free:
- crypto_free_ahash(ctx->acceptor_integ);
- crypto_free_ahash(ctx->initiator_integ);
- crypto_free_ahash(ctx->acceptor_sign);
- crypto_free_ahash(ctx->initiator_sign);
- crypto_free_sync_skcipher(ctx->acceptor_enc_aux);
- crypto_free_sync_skcipher(ctx->acceptor_enc);
- crypto_free_sync_skcipher(ctx->initiator_enc_aux);
- crypto_free_sync_skcipher(ctx->initiator_enc);
- goto out;
+ crypto_free_shash(ctx->acceptor_sign_shash);
+ crypto_free_shash(ctx->initiator_sign_shash);
+ crypto_free_aead(ctx->acceptor_enc_aead);
+ crypto_free_aead(ctx->initiator_enc_aead);
+ return ret;
}
static int
@@ -414,25 +141,17 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
if (IS_ERR(p))
goto out_err;
atomic64_set(&ctx->seq_send64, seq_send64);
- /* set seq_send for use by "older" enctypes */
- atomic_set(&ctx->seq_send, seq_send64);
- if (seq_send64 != atomic_read(&ctx->seq_send)) {
- dprintk("%s: seq_send64 %llx, seq_send %x overflow?\n", __func__,
- seq_send64, atomic_read(&ctx->seq_send));
- p = ERR_PTR(-EINVAL);
- goto out_err;
- }
p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype));
if (IS_ERR(p))
goto out_err;
- ctx->gk5e = gss_krb5_lookup_enctype(ctx->enctype);
- if (ctx->gk5e == NULL) {
+ ctx->krb5e = crypto_krb5_find_enctype(ctx->enctype);
+ if (!ctx->krb5e) {
dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n",
ctx->enctype);
p = ERR_PTR(-EINVAL);
goto out_err;
}
- keylen = ctx->gk5e->keylength;
+ keylen = ctx->krb5e->key_len;
p = simple_get_bytes(p, end, ctx->Ksess, keylen);
if (IS_ERR(p))
@@ -495,21 +214,39 @@ gss_krb5_delete_sec_context(void *internal_ctx)
{
struct krb5_ctx *kctx = internal_ctx;
- crypto_free_sync_skcipher(kctx->seq);
- crypto_free_sync_skcipher(kctx->enc);
- crypto_free_sync_skcipher(kctx->acceptor_enc);
- crypto_free_sync_skcipher(kctx->initiator_enc);
- crypto_free_sync_skcipher(kctx->acceptor_enc_aux);
- crypto_free_sync_skcipher(kctx->initiator_enc_aux);
- crypto_free_ahash(kctx->acceptor_sign);
- crypto_free_ahash(kctx->initiator_sign);
- crypto_free_ahash(kctx->acceptor_integ);
- crypto_free_ahash(kctx->initiator_integ);
+ crypto_free_shash(kctx->acceptor_sign_shash);
+ crypto_free_shash(kctx->initiator_sign_shash);
+ crypto_free_aead(kctx->acceptor_enc_aead);
+ crypto_free_aead(kctx->initiator_enc_aead);
kfree(kctx->mech_used.data);
kfree(kctx);
}
/**
+ * gss_krb5_errno_to_status - Map a negative errno to a GSS major status
+ * @err: negative errno value, or zero
+ *
+ * Returns:
+ * %GSS_S_COMPLETE if @err is zero
+ * %GSS_S_BAD_SIG if @err is -EBADMSG (integrity check failure)
+ * %GSS_S_DEFECTIVE_TOKEN if @err is -EPROTO (malformed token)
+ * %GSS_S_FAILURE for all other negative values
+ */
+u32 gss_krb5_errno_to_status(int err)
+{
+ switch (err) {
+ case 0:
+ return GSS_S_COMPLETE;
+ case -EBADMSG:
+ return GSS_S_BAD_SIG;
+ case -EPROTO:
+ return GSS_S_DEFECTIVE_TOKEN;
+ default:
+ return GSS_S_FAILURE;
+ }
+}
+
+/**
* gss_krb5_get_mic - get_mic for the Kerberos GSS mechanism
* @gctx: GSS context
* @text: plaintext to checksum
@@ -525,7 +262,7 @@ static u32 gss_krb5_get_mic(struct gss_ctx *gctx, struct xdr_buf *text,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->get_mic(kctx, text, token);
+ return gss_krb5_get_mic_v2(kctx, text, token);
}
/**
@@ -547,7 +284,7 @@ static u32 gss_krb5_verify_mic(struct gss_ctx *gctx,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->verify_mic(kctx, message_buffer, read_token);
+ return gss_krb5_verify_mic_v2(kctx, message_buffer, read_token);
}
/**
@@ -567,7 +304,7 @@ static u32 gss_krb5_wrap(struct gss_ctx *gctx, int offset,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->wrap(kctx, offset, buf, pages);
+ return gss_krb5_wrap_v2(kctx, offset, buf, pages);
}
/**
@@ -589,7 +326,7 @@ static u32 gss_krb5_unwrap(struct gss_ctx *gctx, int offset,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->unwrap(kctx, offset, len, buf,
+ return gss_krb5_unwrap_v2(kctx, offset, len, buf,
&gctx->slack, &gctx->align);
}
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c
index ce540df9bce4..cfe066e89f23 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seal.c
@@ -61,9 +61,9 @@
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
-#include <linux/random.h>
-#include <linux/crypto.h>
#include <linux/atomic.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include "gss_krb5_internal.h"
@@ -78,10 +78,10 @@ setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
void *krb5_hdr;
u8 *p, flags = 0x00;
- if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
- flags |= 0x01;
+ if (!ctx->initiate)
+ flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
- flags |= 0x04;
+ flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
/* Per rfc 4121, sec 4.2.6.1, there is no header,
* just start the token.
@@ -97,7 +97,7 @@ setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
*ptr++ = 0xffff;
*ptr = 0xffff;
- token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
+ token->len = GSS_KRB5_TOK_HDR_LEN + ctx->krb5e->cksum_len;
return krb5_hdr;
}
@@ -105,14 +105,17 @@ u32
gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
struct xdr_netobj *token)
{
- struct crypto_ahash *tfm = ctx->initiate ?
- ctx->initiator_sign : ctx->acceptor_sign;
- struct xdr_netobj cksumobj = {
- .len = ctx->gk5e->cksumlength,
- };
+ const struct krb5_enctype *krb5 = ctx->krb5e;
+ struct crypto_shash *shash = ctx->initiate ?
+ ctx->initiator_sign_shash : ctx->acceptor_sign_shash;
+ unsigned int cksum_len = krb5->cksum_len;
+ struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow;
__be64 seq_send_be64;
void *krb5_hdr;
time64_t now;
+ ssize_t ret;
+ int nsg;
dprintk("RPC: %s\n", __func__);
@@ -123,9 +126,25 @@ gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
seq_send_be64 = cpu_to_be64(atomic64_fetch_inc(&ctx->seq_send64));
memcpy(krb5_hdr + 8, (char *) &seq_send_be64, 8);
- cksumobj.data = krb5_hdr + GSS_KRB5_TOK_HDR_LEN;
- if (gss_krb5_checksum(tfm, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
- text, 0, &cksumobj))
+ /*
+ * The checksum is written directly into the token buffer.
+ * This is safe: crypto_krb5_get_mic uses shash (software
+ * hash), so the scatterlist is never DMA-mapped.
+ */
+ nsg = gss_krb5_mic_build_sg(text,
+ krb5_hdr + GSS_KRB5_TOK_HDR_LEN,
+ cksum_len, krb5_hdr,
+ sg_head, &sg_overflow);
+ if (nsg < 0)
+ return GSS_S_FAILURE;
+
+ ret = crypto_krb5_get_mic(krb5, shash, NULL, sg_head, nsg,
+ cksum_len + text->len +
+ GSS_KRB5_TOK_HDR_LEN,
+ cksum_len,
+ text->len + GSS_KRB5_TOK_HDR_LEN);
+ kfree(sg_overflow);
+ if (ret < 0)
return GSS_S_FAILURE;
now = ktime_get_real_seconds();
diff --git a/net/sunrpc/auth_gss/gss_krb5_test.c b/net/sunrpc/auth_gss/gss_krb5_test.c
deleted file mode 100644
index dde1ee934d0d..000000000000
--- a/net/sunrpc/auth_gss/gss_krb5_test.c
+++ /dev/null
@@ -1,1868 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2022 Oracle and/or its affiliates.
- *
- * KUnit test of SunRPC's GSS Kerberos mechanism. Subsystem
- * name is "rpcsec_gss_krb5".
- */
-
-#include <kunit/test.h>
-#include <kunit/visibility.h>
-
-#include <linux/kernel.h>
-#include <crypto/hash.h>
-
-#include <linux/sunrpc/xdr.h>
-#include <linux/sunrpc/gss_krb5.h>
-
-#include "gss_krb5_internal.h"
-
-MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
-
-struct gss_krb5_test_param {
- const char *desc;
- u32 enctype;
- u32 nfold;
- u32 constant;
- const struct xdr_netobj *base_key;
- const struct xdr_netobj *Ke;
- const struct xdr_netobj *usage;
- const struct xdr_netobj *plaintext;
- const struct xdr_netobj *confounder;
- const struct xdr_netobj *expected_result;
- const struct xdr_netobj *expected_hmac;
- const struct xdr_netobj *next_iv;
-};
-
-static inline void gss_krb5_get_desc(const struct gss_krb5_test_param *param,
- char *desc)
-{
- strscpy(desc, param->desc, KUNIT_PARAM_DESC_SIZE);
-}
-
-static void kdf_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj derivedkey;
- int err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- derivedkey.data = kunit_kzalloc(test, param->expected_result->len,
- GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, derivedkey.data);
- derivedkey.len = param->expected_result->len;
-
- /* Act */
- err = gk5e->derive_key(gk5e, param->base_key, &derivedkey,
- param->usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- derivedkey.data,
- derivedkey.len,
- "key mismatch");
-}
-
-static void checksum_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct xdr_buf buf = {
- .head[0].iov_len = param->plaintext->len,
- .len = param->plaintext->len,
- };
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj Kc, checksum;
- struct crypto_ahash *tfm;
- int err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- Kc.len = gk5e->Kc_length;
- Kc.data = kunit_kzalloc(test, Kc.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Kc.data);
- err = gk5e->derive_key(gk5e, param->base_key, &Kc,
- param->usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, tfm);
- err = crypto_ahash_setkey(tfm, Kc.data, Kc.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- buf.head[0].iov_base = kunit_kzalloc(test, buf.head[0].iov_len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf.head[0].iov_base);
- memcpy(buf.head[0].iov_base, param->plaintext->data, buf.head[0].iov_len);
-
- checksum.len = gk5e->cksumlength;
- checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data);
-
- /* Act */
- err = gss_krb5_checksum(tfm, NULL, 0, &buf, 0, &checksum);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- checksum.data,
- checksum.len,
- "checksum mismatch");
-
- crypto_free_ahash(tfm);
-}
-
-#define DEFINE_HEX_XDR_NETOBJ(name, hex_array...) \
- static const u8 name ## _data[] = { hex_array }; \
- static const struct xdr_netobj name = { \
- .data = (u8 *)name##_data, \
- .len = sizeof(name##_data), \
- }
-
-#define DEFINE_STR_XDR_NETOBJ(name, string) \
- static const u8 name ## _str[] = string; \
- static const struct xdr_netobj name = { \
- .data = (u8 *)name##_str, \
- .len = sizeof(name##_str) - 1, \
- }
-
-/*
- * RFC 3961 Appendix A.1. n-fold
- *
- * The n-fold function is defined in section 5.1 of RFC 3961.
- *
- * This test material is copyright (C) The Internet Society (2005).
- */
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test1_plaintext,
- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test1_expected_result,
- 0xbe, 0x07, 0x26, 0x31, 0x27, 0x6b, 0x19, 0x55
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test2_plaintext,
- 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test2_expected_result,
- 0x78, 0xa0, 0x7b, 0x6c, 0xaf, 0x85, 0xfa
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test3_plaintext,
- 0x52, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x43, 0x6f,
- 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2c,
- 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52, 0x75, 0x6e,
- 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x64,
- 0x65
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test3_expected_result,
- 0xbb, 0x6e, 0xd3, 0x08, 0x70, 0xb7, 0xf0, 0xe0
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test4_plaintext,
- 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test4_expected_result,
- 0x59, 0xe4, 0xa8, 0xca, 0x7c, 0x03, 0x85, 0xc3,
- 0xc3, 0x7b, 0x3f, 0x6d, 0x20, 0x00, 0x24, 0x7c,
- 0xb6, 0xe6, 0xbd, 0x5b, 0x3e
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test5_plaintext,
- 0x4d, 0x41, 0x53, 0x53, 0x41, 0x43, 0x48, 0x56,
- 0x53, 0x45, 0x54, 0x54, 0x53, 0x20, 0x49, 0x4e,
- 0x53, 0x54, 0x49, 0x54, 0x56, 0x54, 0x45, 0x20,
- 0x4f, 0x46, 0x20, 0x54, 0x45, 0x43, 0x48, 0x4e,
- 0x4f, 0x4c, 0x4f, 0x47, 0x59
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test5_expected_result,
- 0xdb, 0x3b, 0x0d, 0x8f, 0x0b, 0x06, 0x1e, 0x60,
- 0x32, 0x82, 0xb3, 0x08, 0xa5, 0x08, 0x41, 0x22,
- 0x9a, 0xd7, 0x98, 0xfa, 0xb9, 0x54, 0x0c, 0x1b
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test6_plaintext,
- 0x51
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test6_expected_result,
- 0x51, 0x8a, 0x54, 0xa2, 0x15, 0xa8, 0x45, 0x2a,
- 0x51, 0x8a, 0x54, 0xa2, 0x15, 0xa8, 0x45, 0x2a,
- 0x51, 0x8a, 0x54, 0xa2, 0x15
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test7_plaintext,
- 0x62, 0x61
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test7_expected_result,
- 0xfb, 0x25, 0xd5, 0x31, 0xae, 0x89, 0x74, 0x49,
- 0x9f, 0x52, 0xfd, 0x92, 0xea, 0x98, 0x57, 0xc4,
- 0xba, 0x24, 0xcf, 0x29, 0x7e
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test_kerberos,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test8_expected_result,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test9_expected_result,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73,
- 0x7b, 0x9b, 0x5b, 0x2b, 0x93, 0x13, 0x2b, 0x93
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test10_expected_result,
- 0x83, 0x72, 0xc2, 0x36, 0x34, 0x4e, 0x5f, 0x15,
- 0x50, 0xcd, 0x07, 0x47, 0xe1, 0x5d, 0x62, 0xca,
- 0x7a, 0x5a, 0x3b, 0xce, 0xa4
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test11_expected_result,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73,
- 0x7b, 0x9b, 0x5b, 0x2b, 0x93, 0x13, 0x2b, 0x93,
- 0x5c, 0x9b, 0xdc, 0xda, 0xd9, 0x5c, 0x98, 0x99,
- 0xc4, 0xca, 0xe4, 0xde, 0xe6, 0xd6, 0xca, 0xe4
-);
-
-static const struct gss_krb5_test_param rfc3961_nfold_test_params[] = {
- {
- .desc = "64-fold(\"012345\")",
- .nfold = 64,
- .plaintext = &nfold_test1_plaintext,
- .expected_result = &nfold_test1_expected_result,
- },
- {
- .desc = "56-fold(\"password\")",
- .nfold = 56,
- .plaintext = &nfold_test2_plaintext,
- .expected_result = &nfold_test2_expected_result,
- },
- {
- .desc = "64-fold(\"Rough Consensus, and Running Code\")",
- .nfold = 64,
- .plaintext = &nfold_test3_plaintext,
- .expected_result = &nfold_test3_expected_result,
- },
- {
- .desc = "168-fold(\"password\")",
- .nfold = 168,
- .plaintext = &nfold_test4_plaintext,
- .expected_result = &nfold_test4_expected_result,
- },
- {
- .desc = "192-fold(\"MASSACHVSETTS INSTITVTE OF TECHNOLOGY\")",
- .nfold = 192,
- .plaintext = &nfold_test5_plaintext,
- .expected_result = &nfold_test5_expected_result,
- },
- {
- .desc = "168-fold(\"Q\")",
- .nfold = 168,
- .plaintext = &nfold_test6_plaintext,
- .expected_result = &nfold_test6_expected_result,
- },
- {
- .desc = "168-fold(\"ba\")",
- .nfold = 168,
- .plaintext = &nfold_test7_plaintext,
- .expected_result = &nfold_test7_expected_result,
- },
- {
- .desc = "64-fold(\"kerberos\")",
- .nfold = 64,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test8_expected_result,
- },
- {
- .desc = "128-fold(\"kerberos\")",
- .nfold = 128,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test9_expected_result,
- },
- {
- .desc = "168-fold(\"kerberos\")",
- .nfold = 168,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test10_expected_result,
- },
- {
- .desc = "256-fold(\"kerberos\")",
- .nfold = 256,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test11_expected_result,
- },
-};
-
-/* Creates the function rfc3961_nfold_gen_params */
-KUNIT_ARRAY_PARAM(rfc3961_nfold, rfc3961_nfold_test_params, gss_krb5_get_desc);
-
-static void rfc3961_nfold_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- u8 *result;
-
- /* Arrange */
- result = kunit_kzalloc(test, 4096, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
-
- /* Act */
- krb5_nfold(param->plaintext->len * 8, param->plaintext->data,
- param->expected_result->len * 8, result);
-
- /* Assert */
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- result,
- param->expected_result->len,
- "result mismatch");
-}
-
-static struct kunit_case rfc3961_test_cases[] = {
- {
- .name = "RFC 3961 n-fold",
- .run_case = rfc3961_nfold_case,
- .generate_params = rfc3961_nfold_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc3961_suite = {
- .name = "RFC 3961 tests",
- .test_cases = rfc3961_test_cases,
-};
-
-/*
- * From RFC 3962 Appendix B: Sample Test Vectors
- *
- * Some test vectors for CBC with ciphertext stealing, using an
- * initial vector of all-zero.
- *
- * This test material is copyright (C) The Internet Society (2005).
- */
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_encryption_key,
- 0x63, 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x20,
- 0x74, 0x65, 0x72, 0x69, 0x79, 0x61, 0x6b, 0x69
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_expected_result,
- 0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4,
- 0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f,
- 0x97
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_next_iv,
- 0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4,
- 0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_expected_result,
- 0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1,
- 0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_next_iv,
- 0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1,
- 0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_expected_result,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_next_iv,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43,
- 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20,
- 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_expected_result,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
- 0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c,
- 0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_next_iv,
- 0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c,
- 0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43,
- 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20,
- 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_expected_result,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
- 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
- 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_next_iv,
- 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
- 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43,
- 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20,
- 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20,
- 0x61, 0x6e, 0x64, 0x20, 0x77, 0x6f, 0x6e, 0x74,
- 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x70, 0x2e
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_expected_result,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
- 0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5,
- 0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40,
- 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
- 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_next_iv,
- 0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5,
- 0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40
-);
-
-static const struct gss_krb5_test_param rfc3962_encrypt_test_params[] = {
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 1",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test1_plaintext,
- .expected_result = &rfc3962_enc_test1_expected_result,
- .next_iv = &rfc3962_enc_test1_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 2",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test2_plaintext,
- .expected_result = &rfc3962_enc_test2_expected_result,
- .next_iv = &rfc3962_enc_test2_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 3",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test3_plaintext,
- .expected_result = &rfc3962_enc_test3_expected_result,
- .next_iv = &rfc3962_enc_test3_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 4",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test4_plaintext,
- .expected_result = &rfc3962_enc_test4_expected_result,
- .next_iv = &rfc3962_enc_test4_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 5",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test5_plaintext,
- .expected_result = &rfc3962_enc_test5_expected_result,
- .next_iv = &rfc3962_enc_test5_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 6",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test6_plaintext,
- .expected_result = &rfc3962_enc_test6_expected_result,
- .next_iv = &rfc3962_enc_test6_next_iv,
- },
-};
-
-/* Creates the function rfc3962_encrypt_gen_params */
-KUNIT_ARRAY_PARAM(rfc3962_encrypt, rfc3962_encrypt_test_params,
- gss_krb5_get_desc);
-
-/*
- * This tests the implementation of the encryption part of the mechanism.
- * It does not apply a confounder or test the result of HMAC over the
- * plaintext.
- */
-static void rfc3962_encrypt_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_buf buf;
- void *iv, *text;
- u32 err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- iv = kunit_kzalloc(test, crypto_sync_skcipher_ivsize(cts_tfm), GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, iv);
-
- text = kunit_kzalloc(test, param->plaintext->len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
-
- memcpy(text, param->plaintext->data, param->plaintext->len);
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- /* Act */
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL,
- iv, crypto_sync_skcipher_ivsize(cts_tfm));
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test,
- param->expected_result->len, buf.len,
- "ciphertext length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- text,
- param->expected_result->len,
- "ciphertext mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->next_iv->data,
- iv,
- param->next_iv->len,
- "IV mismatch");
-
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case rfc3962_test_cases[] = {
- {
- .name = "RFC 3962 encryption",
- .run_case = rfc3962_encrypt_case,
- .generate_params = rfc3962_encrypt_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc3962_suite = {
- .name = "RFC 3962 suite",
- .test_cases = rfc3962_test_cases,
-};
-
-/*
- * From RFC 6803 Section 10. Test vectors
- *
- * Sample results for key derivation
- *
- * Copyright (c) 2012 IETF Trust and the persons identified as the
- * document authors. All rights reserved.
- */
-
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_basekey,
- 0x57, 0xd0, 0x29, 0x72, 0x98, 0xff, 0xd9, 0xd3,
- 0x5d, 0xe5, 0xa4, 0x7f, 0xb4, 0xbd, 0xe2, 0x4b
-);
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Kc,
- 0xd1, 0x55, 0x77, 0x5a, 0x20, 0x9d, 0x05, 0xf0,
- 0x2b, 0x38, 0xd4, 0x2a, 0x38, 0x9e, 0x5a, 0x56
-);
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Ke,
- 0x64, 0xdf, 0x83, 0xf8, 0x5a, 0x53, 0x2f, 0x17,
- 0x57, 0x7d, 0x8c, 0x37, 0x03, 0x57, 0x96, 0xab
-);
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Ki,
- 0x3e, 0x4f, 0xbd, 0xf3, 0x0f, 0xb8, 0x25, 0x9c,
- 0x42, 0x5c, 0xb6, 0xc9, 0x6f, 0x1f, 0x46, 0x35
-);
-
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_basekey,
- 0xb9, 0xd6, 0x82, 0x8b, 0x20, 0x56, 0xb7, 0xbe,
- 0x65, 0x6d, 0x88, 0xa1, 0x23, 0xb1, 0xfa, 0xc6,
- 0x82, 0x14, 0xac, 0x2b, 0x72, 0x7e, 0xcf, 0x5f,
- 0x69, 0xaf, 0xe0, 0xc4, 0xdf, 0x2a, 0x6d, 0x2c
-);
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Kc,
- 0xe4, 0x67, 0xf9, 0xa9, 0x55, 0x2b, 0xc7, 0xd3,
- 0x15, 0x5a, 0x62, 0x20, 0xaf, 0x9c, 0x19, 0x22,
- 0x0e, 0xee, 0xd4, 0xff, 0x78, 0xb0, 0xd1, 0xe6,
- 0xa1, 0x54, 0x49, 0x91, 0x46, 0x1a, 0x9e, 0x50
-);
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Ke,
- 0x41, 0x2a, 0xef, 0xc3, 0x62, 0xa7, 0x28, 0x5f,
- 0xc3, 0x96, 0x6c, 0x6a, 0x51, 0x81, 0xe7, 0x60,
- 0x5a, 0xe6, 0x75, 0x23, 0x5b, 0x6d, 0x54, 0x9f,
- 0xbf, 0xc9, 0xab, 0x66, 0x30, 0xa4, 0xc6, 0x04
-);
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Ki,
- 0xfa, 0x62, 0x4f, 0xa0, 0xe5, 0x23, 0x99, 0x3f,
- 0xa3, 0x88, 0xae, 0xfd, 0xc6, 0x7e, 0x67, 0xeb,
- 0xcd, 0x8c, 0x08, 0xe8, 0xa0, 0x24, 0x6b, 0x1d,
- 0x73, 0xb0, 0xd1, 0xdd, 0x9f, 0xc5, 0x82, 0xb0
-);
-
-DEFINE_HEX_XDR_NETOBJ(usage_checksum,
- 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(usage_encryption,
- 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_ENCRYPTION
-);
-DEFINE_HEX_XDR_NETOBJ(usage_integrity,
- 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_INTEGRITY
-);
-
-static const struct gss_krb5_test_param rfc6803_kdf_test_params[] = {
- {
- .desc = "Derive Kc subkey for camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &camellia128_cts_cmac_basekey,
- .usage = &usage_checksum,
- .expected_result = &camellia128_cts_cmac_Kc,
- },
- {
- .desc = "Derive Ke subkey for camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &camellia128_cts_cmac_basekey,
- .usage = &usage_encryption,
- .expected_result = &camellia128_cts_cmac_Ke,
- },
- {
- .desc = "Derive Ki subkey for camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &camellia128_cts_cmac_basekey,
- .usage = &usage_integrity,
- .expected_result = &camellia128_cts_cmac_Ki,
- },
- {
- .desc = "Derive Kc subkey for camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &camellia256_cts_cmac_basekey,
- .usage = &usage_checksum,
- .expected_result = &camellia256_cts_cmac_Kc,
- },
- {
- .desc = "Derive Ke subkey for camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &camellia256_cts_cmac_basekey,
- .usage = &usage_encryption,
- .expected_result = &camellia256_cts_cmac_Ke,
- },
- {
- .desc = "Derive Ki subkey for camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &camellia256_cts_cmac_basekey,
- .usage = &usage_integrity,
- .expected_result = &camellia256_cts_cmac_Ki,
- },
-};
-
-/* Creates the function rfc6803_kdf_gen_params */
-KUNIT_ARRAY_PARAM(rfc6803_kdf, rfc6803_kdf_test_params, gss_krb5_get_desc);
-
-/*
- * From RFC 6803 Section 10. Test vectors
- *
- * Sample checksums.
- *
- * Copyright (c) 2012 IETF Trust and the persons identified as the
- * document authors. All rights reserved.
- *
- * XXX: These tests are likely to fail on EBCDIC or Unicode platforms.
- */
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test1_plaintext,
- "abcdefghijk");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_basekey,
- 0x1d, 0xc4, 0x6a, 0x8d, 0x76, 0x3f, 0x4f, 0x93,
- 0x74, 0x2b, 0xcb, 0xa3, 0x38, 0x75, 0x76, 0xc3
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_usage,
- 0x00, 0x00, 0x00, 0x07, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_expected_result,
- 0x11, 0x78, 0xe6, 0xc5, 0xc4, 0x7a, 0x8c, 0x1a,
- 0xe0, 0xc4, 0xb9, 0xc7, 0xd4, 0xeb, 0x7b, 0x6b
-);
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test2_plaintext,
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_basekey,
- 0x50, 0x27, 0xbc, 0x23, 0x1d, 0x0f, 0x3a, 0x9d,
- 0x23, 0x33, 0x3f, 0x1c, 0xa6, 0xfd, 0xbe, 0x7c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_usage,
- 0x00, 0x00, 0x00, 0x08, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_expected_result,
- 0xd1, 0xb3, 0x4f, 0x70, 0x04, 0xa7, 0x31, 0xf2,
- 0x3a, 0x0c, 0x00, 0xbf, 0x6c, 0x3f, 0x75, 0x3a
-);
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test3_plaintext,
- "123456789");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_basekey,
- 0xb6, 0x1c, 0x86, 0xcc, 0x4e, 0x5d, 0x27, 0x57,
- 0x54, 0x5a, 0xd4, 0x23, 0x39, 0x9f, 0xb7, 0x03,
- 0x1e, 0xca, 0xb9, 0x13, 0xcb, 0xb9, 0x00, 0xbd,
- 0x7a, 0x3c, 0x6d, 0xd8, 0xbf, 0x92, 0x01, 0x5b
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_usage,
- 0x00, 0x00, 0x00, 0x09, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_expected_result,
- 0x87, 0xa1, 0x2c, 0xfd, 0x2b, 0x96, 0x21, 0x48,
- 0x10, 0xf0, 0x1c, 0x82, 0x6e, 0x77, 0x44, 0xb1
-);
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test4_plaintext,
- "!@#$%^&*()!@#$%^&*()!@#$%^&*()");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_basekey,
- 0x32, 0x16, 0x4c, 0x5b, 0x43, 0x4d, 0x1d, 0x15,
- 0x38, 0xe4, 0xcf, 0xd9, 0xbe, 0x80, 0x40, 0xfe,
- 0x8c, 0x4a, 0xc7, 0xac, 0xc4, 0xb9, 0x3d, 0x33,
- 0x14, 0xd2, 0x13, 0x36, 0x68, 0x14, 0x7a, 0x05
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_usage,
- 0x00, 0x00, 0x00, 0x0a, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_expected_result,
- 0x3f, 0xa0, 0xb4, 0x23, 0x55, 0xe5, 0x2b, 0x18,
- 0x91, 0x87, 0x29, 0x4a, 0xa2, 0x52, 0xab, 0x64
-);
-
-static const struct gss_krb5_test_param rfc6803_checksum_test_params[] = {
- {
- .desc = "camellia128-cts-cmac checksum test 1",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &rfc6803_checksum_test1_basekey,
- .usage = &rfc6803_checksum_test1_usage,
- .plaintext = &rfc6803_checksum_test1_plaintext,
- .expected_result = &rfc6803_checksum_test1_expected_result,
- },
- {
- .desc = "camellia128-cts-cmac checksum test 2",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &rfc6803_checksum_test2_basekey,
- .usage = &rfc6803_checksum_test2_usage,
- .plaintext = &rfc6803_checksum_test2_plaintext,
- .expected_result = &rfc6803_checksum_test2_expected_result,
- },
- {
- .desc = "camellia256-cts-cmac checksum test 3",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &rfc6803_checksum_test3_basekey,
- .usage = &rfc6803_checksum_test3_usage,
- .plaintext = &rfc6803_checksum_test3_plaintext,
- .expected_result = &rfc6803_checksum_test3_expected_result,
- },
- {
- .desc = "camellia256-cts-cmac checksum test 4",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &rfc6803_checksum_test4_basekey,
- .usage = &rfc6803_checksum_test4_usage,
- .plaintext = &rfc6803_checksum_test4_plaintext,
- .expected_result = &rfc6803_checksum_test4_expected_result,
- },
-};
-
-/* Creates the function rfc6803_checksum_gen_params */
-KUNIT_ARRAY_PARAM(rfc6803_checksum, rfc6803_checksum_test_params,
- gss_krb5_get_desc);
-
-/*
- * From RFC 6803 Section 10. Test vectors
- *
- * Sample encryptions (all using the default cipher state)
- *
- * Copyright (c) 2012 IETF Trust and the persons identified as the
- * document authors. All rights reserved.
- *
- * Key usage values are from errata 4326 against RFC 6803.
- */
-
-static const struct xdr_netobj rfc6803_enc_empty_plaintext = {
- .len = 0,
-};
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_1byte_plaintext, "1");
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_9byte_plaintext, "9 bytesss");
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_13byte_plaintext, "13 bytes byte");
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_30byte_plaintext,
- "30 bytes bytes bytes bytes byt"
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_confounder,
- 0xb6, 0x98, 0x22, 0xa1, 0x9a, 0x6b, 0x09, 0xc0,
- 0xeb, 0xc8, 0x55, 0x7d, 0x1f, 0x1b, 0x6c, 0x0a
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_basekey,
- 0x1d, 0xc4, 0x6a, 0x8d, 0x76, 0x3f, 0x4f, 0x93,
- 0x74, 0x2b, 0xcb, 0xa3, 0x38, 0x75, 0x76, 0xc3
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_expected_result,
- 0xc4, 0x66, 0xf1, 0x87, 0x10, 0x69, 0x92, 0x1e,
- 0xdb, 0x7c, 0x6f, 0xde, 0x24, 0x4a, 0x52, 0xdb,
- 0x0b, 0xa1, 0x0e, 0xdc, 0x19, 0x7b, 0xdb, 0x80,
- 0x06, 0x65, 0x8c, 0xa3, 0xcc, 0xce, 0x6e, 0xb8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_confounder,
- 0x6f, 0x2f, 0xc3, 0xc2, 0xa1, 0x66, 0xfd, 0x88,
- 0x98, 0x96, 0x7a, 0x83, 0xde, 0x95, 0x96, 0xd9
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_basekey,
- 0x50, 0x27, 0xbc, 0x23, 0x1d, 0x0f, 0x3a, 0x9d,
- 0x23, 0x33, 0x3f, 0x1c, 0xa6, 0xfd, 0xbe, 0x7c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_expected_result,
- 0x84, 0x2d, 0x21, 0xfd, 0x95, 0x03, 0x11, 0xc0,
- 0xdd, 0x46, 0x4a, 0x3f, 0x4b, 0xe8, 0xd6, 0xda,
- 0x88, 0xa5, 0x6d, 0x55, 0x9c, 0x9b, 0x47, 0xd3,
- 0xf9, 0xa8, 0x50, 0x67, 0xaf, 0x66, 0x15, 0x59,
- 0xb8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_confounder,
- 0xa5, 0xb4, 0xa7, 0x1e, 0x07, 0x7a, 0xee, 0xf9,
- 0x3c, 0x87, 0x63, 0xc1, 0x8f, 0xdb, 0x1f, 0x10
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_basekey,
- 0xa1, 0xbb, 0x61, 0xe8, 0x05, 0xf9, 0xba, 0x6d,
- 0xde, 0x8f, 0xdb, 0xdd, 0xc0, 0x5c, 0xde, 0xa0
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_expected_result,
- 0x61, 0x9f, 0xf0, 0x72, 0xe3, 0x62, 0x86, 0xff,
- 0x0a, 0x28, 0xde, 0xb3, 0xa3, 0x52, 0xec, 0x0d,
- 0x0e, 0xdf, 0x5c, 0x51, 0x60, 0xd6, 0x63, 0xc9,
- 0x01, 0x75, 0x8c, 0xcf, 0x9d, 0x1e, 0xd3, 0x3d,
- 0x71, 0xdb, 0x8f, 0x23, 0xaa, 0xbf, 0x83, 0x48,
- 0xa0
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_confounder,
- 0x19, 0xfe, 0xe4, 0x0d, 0x81, 0x0c, 0x52, 0x4b,
- 0x5b, 0x22, 0xf0, 0x18, 0x74, 0xc6, 0x93, 0xda
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_basekey,
- 0x2c, 0xa2, 0x7a, 0x5f, 0xaf, 0x55, 0x32, 0x24,
- 0x45, 0x06, 0x43, 0x4e, 0x1c, 0xef, 0x66, 0x76
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_expected_result,
- 0xb8, 0xec, 0xa3, 0x16, 0x7a, 0xe6, 0x31, 0x55,
- 0x12, 0xe5, 0x9f, 0x98, 0xa7, 0xc5, 0x00, 0x20,
- 0x5e, 0x5f, 0x63, 0xff, 0x3b, 0xb3, 0x89, 0xaf,
- 0x1c, 0x41, 0xa2, 0x1d, 0x64, 0x0d, 0x86, 0x15,
- 0xc9, 0xed, 0x3f, 0xbe, 0xb0, 0x5a, 0xb6, 0xac,
- 0xb6, 0x76, 0x89, 0xb5, 0xea
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_confounder,
- 0xca, 0x7a, 0x7a, 0xb4, 0xbe, 0x19, 0x2d, 0xab,
- 0xd6, 0x03, 0x50, 0x6d, 0xb1, 0x9c, 0x39, 0xe2
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_basekey,
- 0x78, 0x24, 0xf8, 0xc1, 0x6f, 0x83, 0xff, 0x35,
- 0x4c, 0x6b, 0xf7, 0x51, 0x5b, 0x97, 0x3f, 0x43
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_expected_result,
- 0xa2, 0x6a, 0x39, 0x05, 0xa4, 0xff, 0xd5, 0x81,
- 0x6b, 0x7b, 0x1e, 0x27, 0x38, 0x0d, 0x08, 0x09,
- 0x0c, 0x8e, 0xc1, 0xf3, 0x04, 0x49, 0x6e, 0x1a,
- 0xbd, 0xcd, 0x2b, 0xdc, 0xd1, 0xdf, 0xfc, 0x66,
- 0x09, 0x89, 0xe1, 0x17, 0xa7, 0x13, 0xdd, 0xbb,
- 0x57, 0xa4, 0x14, 0x6c, 0x15, 0x87, 0xcb, 0xa4,
- 0x35, 0x66, 0x65, 0x59, 0x1d, 0x22, 0x40, 0x28,
- 0x2f, 0x58, 0x42, 0xb1, 0x05, 0xa5
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_confounder,
- 0x3c, 0xbb, 0xd2, 0xb4, 0x59, 0x17, 0x94, 0x10,
- 0x67, 0xf9, 0x65, 0x99, 0xbb, 0x98, 0x92, 0x6c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_basekey,
- 0xb6, 0x1c, 0x86, 0xcc, 0x4e, 0x5d, 0x27, 0x57,
- 0x54, 0x5a, 0xd4, 0x23, 0x39, 0x9f, 0xb7, 0x03,
- 0x1e, 0xca, 0xb9, 0x13, 0xcb, 0xb9, 0x00, 0xbd,
- 0x7a, 0x3c, 0x6d, 0xd8, 0xbf, 0x92, 0x01, 0x5b
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_expected_result,
- 0x03, 0x88, 0x6d, 0x03, 0x31, 0x0b, 0x47, 0xa6,
- 0xd8, 0xf0, 0x6d, 0x7b, 0x94, 0xd1, 0xdd, 0x83,
- 0x7e, 0xcc, 0xe3, 0x15, 0xef, 0x65, 0x2a, 0xff,
- 0x62, 0x08, 0x59, 0xd9, 0x4a, 0x25, 0x92, 0x66
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_confounder,
- 0xde, 0xf4, 0x87, 0xfc, 0xeb, 0xe6, 0xde, 0x63,
- 0x46, 0xd4, 0xda, 0x45, 0x21, 0xbb, 0xa2, 0xd2
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_basekey,
- 0x1b, 0x97, 0xfe, 0x0a, 0x19, 0x0e, 0x20, 0x21,
- 0xeb, 0x30, 0x75, 0x3e, 0x1b, 0x6e, 0x1e, 0x77,
- 0xb0, 0x75, 0x4b, 0x1d, 0x68, 0x46, 0x10, 0x35,
- 0x58, 0x64, 0x10, 0x49, 0x63, 0x46, 0x38, 0x33
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_expected_result,
- 0x2c, 0x9c, 0x15, 0x70, 0x13, 0x3c, 0x99, 0xbf,
- 0x6a, 0x34, 0xbc, 0x1b, 0x02, 0x12, 0x00, 0x2f,
- 0xd1, 0x94, 0x33, 0x87, 0x49, 0xdb, 0x41, 0x35,
- 0x49, 0x7a, 0x34, 0x7c, 0xfc, 0xd9, 0xd1, 0x8a,
- 0x12
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_confounder,
- 0xad, 0x4f, 0xf9, 0x04, 0xd3, 0x4e, 0x55, 0x53,
- 0x84, 0xb1, 0x41, 0x00, 0xfc, 0x46, 0x5f, 0x88
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_basekey,
- 0x32, 0x16, 0x4c, 0x5b, 0x43, 0x4d, 0x1d, 0x15,
- 0x38, 0xe4, 0xcf, 0xd9, 0xbe, 0x80, 0x40, 0xfe,
- 0x8c, 0x4a, 0xc7, 0xac, 0xc4, 0xb9, 0x3d, 0x33,
- 0x14, 0xd2, 0x13, 0x36, 0x68, 0x14, 0x7a, 0x05
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_expected_result,
- 0x9c, 0x6d, 0xe7, 0x5f, 0x81, 0x2d, 0xe7, 0xed,
- 0x0d, 0x28, 0xb2, 0x96, 0x35, 0x57, 0xa1, 0x15,
- 0x64, 0x09, 0x98, 0x27, 0x5b, 0x0a, 0xf5, 0x15,
- 0x27, 0x09, 0x91, 0x3f, 0xf5, 0x2a, 0x2a, 0x9c,
- 0x8e, 0x63, 0xb8, 0x72, 0xf9, 0x2e, 0x64, 0xc8,
- 0x39
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_confounder,
- 0xcf, 0x9b, 0xca, 0x6d, 0xf1, 0x14, 0x4e, 0x0c,
- 0x0a, 0xf9, 0xb8, 0xf3, 0x4c, 0x90, 0xd5, 0x14
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_basekey,
- 0xb0, 0x38, 0xb1, 0x32, 0xcd, 0x8e, 0x06, 0x61,
- 0x22, 0x67, 0xfa, 0xb7, 0x17, 0x00, 0x66, 0xd8,
- 0x8a, 0xec, 0xcb, 0xa0, 0xb7, 0x44, 0xbf, 0xc6,
- 0x0d, 0xc8, 0x9b, 0xca, 0x18, 0x2d, 0x07, 0x15
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_expected_result,
- 0xee, 0xec, 0x85, 0xa9, 0x81, 0x3c, 0xdc, 0x53,
- 0x67, 0x72, 0xab, 0x9b, 0x42, 0xde, 0xfc, 0x57,
- 0x06, 0xf7, 0x26, 0xe9, 0x75, 0xdd, 0xe0, 0x5a,
- 0x87, 0xeb, 0x54, 0x06, 0xea, 0x32, 0x4c, 0xa1,
- 0x85, 0xc9, 0x98, 0x6b, 0x42, 0xaa, 0xbe, 0x79,
- 0x4b, 0x84, 0x82, 0x1b, 0xee
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_confounder,
- 0x64, 0x4d, 0xef, 0x38, 0xda, 0x35, 0x00, 0x72,
- 0x75, 0x87, 0x8d, 0x21, 0x68, 0x55, 0xe2, 0x28
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_basekey,
- 0xcc, 0xfc, 0xd3, 0x49, 0xbf, 0x4c, 0x66, 0x77,
- 0xe8, 0x6e, 0x4b, 0x02, 0xb8, 0xea, 0xb9, 0x24,
- 0xa5, 0x46, 0xac, 0x73, 0x1c, 0xf9, 0xbf, 0x69,
- 0x89, 0xb9, 0x96, 0xe7, 0xd6, 0xbf, 0xbb, 0xa7
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_expected_result,
- 0x0e, 0x44, 0x68, 0x09, 0x85, 0x85, 0x5f, 0x2d,
- 0x1f, 0x18, 0x12, 0x52, 0x9c, 0xa8, 0x3b, 0xfd,
- 0x8e, 0x34, 0x9d, 0xe6, 0xfd, 0x9a, 0xda, 0x0b,
- 0xaa, 0xa0, 0x48, 0xd6, 0x8e, 0x26, 0x5f, 0xeb,
- 0xf3, 0x4a, 0xd1, 0x25, 0x5a, 0x34, 0x49, 0x99,
- 0xad, 0x37, 0x14, 0x68, 0x87, 0xa6, 0xc6, 0x84,
- 0x57, 0x31, 0xac, 0x7f, 0x46, 0x37, 0x6a, 0x05,
- 0x04, 0xcd, 0x06, 0x57, 0x14, 0x74
-);
-
-static const struct gss_krb5_test_param rfc6803_encrypt_test_params[] = {
- {
- .desc = "Encrypt empty plaintext with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 0,
- .base_key = &rfc6803_enc_test1_basekey,
- .plaintext = &rfc6803_enc_empty_plaintext,
- .confounder = &rfc6803_enc_test1_confounder,
- .expected_result = &rfc6803_enc_test1_expected_result,
- },
- {
- .desc = "Encrypt 1 byte with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 1,
- .base_key = &rfc6803_enc_test2_basekey,
- .plaintext = &rfc6803_enc_1byte_plaintext,
- .confounder = &rfc6803_enc_test2_confounder,
- .expected_result = &rfc6803_enc_test2_expected_result,
- },
- {
- .desc = "Encrypt 9 bytes with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 2,
- .base_key = &rfc6803_enc_test3_basekey,
- .plaintext = &rfc6803_enc_9byte_plaintext,
- .confounder = &rfc6803_enc_test3_confounder,
- .expected_result = &rfc6803_enc_test3_expected_result,
- },
- {
- .desc = "Encrypt 13 bytes with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 3,
- .base_key = &rfc6803_enc_test4_basekey,
- .plaintext = &rfc6803_enc_13byte_plaintext,
- .confounder = &rfc6803_enc_test4_confounder,
- .expected_result = &rfc6803_enc_test4_expected_result,
- },
- {
- .desc = "Encrypt 30 bytes with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 4,
- .base_key = &rfc6803_enc_test5_basekey,
- .plaintext = &rfc6803_enc_30byte_plaintext,
- .confounder = &rfc6803_enc_test5_confounder,
- .expected_result = &rfc6803_enc_test5_expected_result,
- },
- {
- .desc = "Encrypt empty plaintext with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 0,
- .base_key = &rfc6803_enc_test6_basekey,
- .plaintext = &rfc6803_enc_empty_plaintext,
- .confounder = &rfc6803_enc_test6_confounder,
- .expected_result = &rfc6803_enc_test6_expected_result,
- },
- {
- .desc = "Encrypt 1 byte with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 1,
- .base_key = &rfc6803_enc_test7_basekey,
- .plaintext = &rfc6803_enc_1byte_plaintext,
- .confounder = &rfc6803_enc_test7_confounder,
- .expected_result = &rfc6803_enc_test7_expected_result,
- },
- {
- .desc = "Encrypt 9 bytes with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 2,
- .base_key = &rfc6803_enc_test8_basekey,
- .plaintext = &rfc6803_enc_9byte_plaintext,
- .confounder = &rfc6803_enc_test8_confounder,
- .expected_result = &rfc6803_enc_test8_expected_result,
- },
- {
- .desc = "Encrypt 13 bytes with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 3,
- .base_key = &rfc6803_enc_test9_basekey,
- .plaintext = &rfc6803_enc_13byte_plaintext,
- .confounder = &rfc6803_enc_test9_confounder,
- .expected_result = &rfc6803_enc_test9_expected_result,
- },
- {
- .desc = "Encrypt 30 bytes with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 4,
- .base_key = &rfc6803_enc_test10_basekey,
- .plaintext = &rfc6803_enc_30byte_plaintext,
- .confounder = &rfc6803_enc_test10_confounder,
- .expected_result = &rfc6803_enc_test10_expected_result,
- },
-};
-
-/* Creates the function rfc6803_encrypt_gen_params */
-KUNIT_ARRAY_PARAM(rfc6803_encrypt, rfc6803_encrypt_test_params,
- gss_krb5_get_desc);
-
-static void rfc6803_encrypt_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj Ke, Ki, checksum;
- u8 usage_data[GSS_KRB5_K5CLENGTH];
- struct xdr_netobj usage = {
- .data = usage_data,
- .len = sizeof(usage_data),
- };
- struct crypto_ahash *ahash_tfm;
- unsigned int blocksize;
- struct xdr_buf buf;
- void *text;
- size_t len;
- u32 err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- memset(usage_data, 0, sizeof(usage_data));
- usage.data[3] = param->constant;
-
- Ke.len = gk5e->Ke_length;
- Ke.data = kunit_kzalloc(test, Ke.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ke.data);
- usage.data[4] = KEY_USAGE_SEED_ENCRYPTION;
- err = gk5e->derive_key(gk5e, param->base_key, &Ke, &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
- blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
-
- len = param->confounder->len + param->plaintext->len + blocksize;
- text = kunit_kzalloc(test, len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
- memcpy(text, param->confounder->data, param->confounder->len);
- memcpy(text + param->confounder->len, param->plaintext->data,
- param->plaintext->len);
-
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->confounder->len + param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- checksum.len = gk5e->cksumlength;
- checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data);
-
- Ki.len = gk5e->Ki_length;
- Ki.data = kunit_kzalloc(test, Ki.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ki.data);
- usage.data[4] = KEY_USAGE_SEED_INTEGRITY;
- err = gk5e->derive_key(gk5e, param->base_key, &Ki,
- &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
- ahash_tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ahash_tfm);
- err = crypto_ahash_setkey(ahash_tfm, Ki.data, Ki.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Act */
- err = gss_krb5_checksum(ahash_tfm, NULL, 0, &buf, 0, &checksum);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test, param->expected_result->len,
- buf.len + checksum.len,
- "ciphertext length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- buf.head[0].iov_base,
- buf.len,
- "encrypted result mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data +
- (param->expected_result->len - checksum.len),
- checksum.data,
- checksum.len,
- "HMAC mismatch");
-
- crypto_free_ahash(ahash_tfm);
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case rfc6803_test_cases[] = {
- {
- .name = "RFC 6803 key derivation",
- .run_case = kdf_case,
- .generate_params = rfc6803_kdf_gen_params,
- },
- {
- .name = "RFC 6803 checksum",
- .run_case = checksum_case,
- .generate_params = rfc6803_checksum_gen_params,
- },
- {
- .name = "RFC 6803 encryption",
- .run_case = rfc6803_encrypt_case,
- .generate_params = rfc6803_encrypt_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc6803_suite = {
- .name = "RFC 6803 suite",
- .test_cases = rfc6803_test_cases,
-};
-
-/*
- * From RFC 8009 Appendix A. Test Vectors
- *
- * Sample results for SHA-2 enctype key derivation
- *
- * This test material is copyright (c) 2016 IETF Trust and the
- * persons identified as the document authors. All rights reserved.
- */
-
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_basekey,
- 0x37, 0x05, 0xd9, 0x60, 0x80, 0xc1, 0x77, 0x28,
- 0xa0, 0xe8, 0x00, 0xea, 0xb6, 0xe0, 0xd2, 0x3c
-);
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Kc,
- 0xb3, 0x1a, 0x01, 0x8a, 0x48, 0xf5, 0x47, 0x76,
- 0xf4, 0x03, 0xe9, 0xa3, 0x96, 0x32, 0x5d, 0xc3
-);
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Ke,
- 0x9b, 0x19, 0x7d, 0xd1, 0xe8, 0xc5, 0x60, 0x9d,
- 0x6e, 0x67, 0xc3, 0xe3, 0x7c, 0x62, 0xc7, 0x2e
-);
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Ki,
- 0x9f, 0xda, 0x0e, 0x56, 0xab, 0x2d, 0x85, 0xe1,
- 0x56, 0x9a, 0x68, 0x86, 0x96, 0xc2, 0x6a, 0x6c
-);
-
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_basekey,
- 0x6d, 0x40, 0x4d, 0x37, 0xfa, 0xf7, 0x9f, 0x9d,
- 0xf0, 0xd3, 0x35, 0x68, 0xd3, 0x20, 0x66, 0x98,
- 0x00, 0xeb, 0x48, 0x36, 0x47, 0x2e, 0xa8, 0xa0,
- 0x26, 0xd1, 0x6b, 0x71, 0x82, 0x46, 0x0c, 0x52
-);
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Kc,
- 0xef, 0x57, 0x18, 0xbe, 0x86, 0xcc, 0x84, 0x96,
- 0x3d, 0x8b, 0xbb, 0x50, 0x31, 0xe9, 0xf5, 0xc4,
- 0xba, 0x41, 0xf2, 0x8f, 0xaf, 0x69, 0xe7, 0x3d
-);
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Ke,
- 0x56, 0xab, 0x22, 0xbe, 0xe6, 0x3d, 0x82, 0xd7,
- 0xbc, 0x52, 0x27, 0xf6, 0x77, 0x3f, 0x8e, 0xa7,
- 0xa5, 0xeb, 0x1c, 0x82, 0x51, 0x60, 0xc3, 0x83,
- 0x12, 0x98, 0x0c, 0x44, 0x2e, 0x5c, 0x7e, 0x49
-);
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Ki,
- 0x69, 0xb1, 0x65, 0x14, 0xe3, 0xcd, 0x8e, 0x56,
- 0xb8, 0x20, 0x10, 0xd5, 0xc7, 0x30, 0x12, 0xb6,
- 0x22, 0xc4, 0xd0, 0x0f, 0xfc, 0x23, 0xed, 0x1f
-);
-
-static const struct gss_krb5_test_param rfc8009_kdf_test_params[] = {
- {
- .desc = "Derive Kc subkey for aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_checksum,
- .expected_result = &aes128_cts_hmac_sha256_128_Kc,
- },
- {
- .desc = "Derive Ke subkey for aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_encryption,
- .expected_result = &aes128_cts_hmac_sha256_128_Ke,
- },
- {
- .desc = "Derive Ki subkey for aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_integrity,
- .expected_result = &aes128_cts_hmac_sha256_128_Ki,
- },
- {
- .desc = "Derive Kc subkey for aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_checksum,
- .expected_result = &aes256_cts_hmac_sha384_192_Kc,
- },
- {
- .desc = "Derive Ke subkey for aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_encryption,
- .expected_result = &aes256_cts_hmac_sha384_192_Ke,
- },
- {
- .desc = "Derive Ki subkey for aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_integrity,
- .expected_result = &aes256_cts_hmac_sha384_192_Ki,
- },
-};
-
-/* Creates the function rfc8009_kdf_gen_params */
-KUNIT_ARRAY_PARAM(rfc8009_kdf, rfc8009_kdf_test_params, gss_krb5_get_desc);
-
-/*
- * From RFC 8009 Appendix A. Test Vectors
- *
- * These sample checksums use the above sample key derivation results,
- * including use of the same base-key and key usage values.
- *
- * This test material is copyright (c) 2016 IETF Trust and the
- * persons identified as the document authors. All rights reserved.
- */
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_test1_expected_result,
- 0xd7, 0x83, 0x67, 0x18, 0x66, 0x43, 0xd6, 0x7b,
- 0x41, 0x1c, 0xba, 0x91, 0x39, 0xfc, 0x1d, 0xee
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_test2_expected_result,
- 0x45, 0xee, 0x79, 0x15, 0x67, 0xee, 0xfc, 0xa3,
- 0x7f, 0x4a, 0xc1, 0xe0, 0x22, 0x2d, 0xe8, 0x0d,
- 0x43, 0xc3, 0xbf, 0xa0, 0x66, 0x99, 0x67, 0x2a
-);
-
-static const struct gss_krb5_test_param rfc8009_checksum_test_params[] = {
- {
- .desc = "Checksum with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_checksum,
- .plaintext = &rfc8009_checksum_plaintext,
- .expected_result = &rfc8009_checksum_test1_expected_result,
- },
- {
- .desc = "Checksum with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_checksum,
- .plaintext = &rfc8009_checksum_plaintext,
- .expected_result = &rfc8009_checksum_test2_expected_result,
- },
-};
-
-/* Creates the function rfc8009_checksum_gen_params */
-KUNIT_ARRAY_PARAM(rfc8009_checksum, rfc8009_checksum_test_params,
- gss_krb5_get_desc);
-
-/*
- * From RFC 8009 Appendix A. Test Vectors
- *
- * Sample encryptions (all using the default cipher state):
- * --------------------------------------------------------
- *
- * These sample encryptions use the above sample key derivation results,
- * including use of the same base-key and key usage values.
- *
- * This test material is copyright (c) 2016 IETF Trust and the
- * persons identified as the document authors. All rights reserved.
- */
-
-static const struct xdr_netobj rfc8009_enc_empty_plaintext = {
- .len = 0,
-};
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_short_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_block_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_long_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_confounder,
- 0x7e, 0x58, 0x95, 0xea, 0xf2, 0x67, 0x24, 0x35,
- 0xba, 0xd8, 0x17, 0xf5, 0x45, 0xa3, 0x71, 0x48
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_expected_result,
- 0xef, 0x85, 0xfb, 0x89, 0x0b, 0xb8, 0x47, 0x2f,
- 0x4d, 0xab, 0x20, 0x39, 0x4d, 0xca, 0x78, 0x1d
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_expected_hmac,
- 0xad, 0x87, 0x7e, 0xda, 0x39, 0xd5, 0x0c, 0x87,
- 0x0c, 0x0d, 0x5a, 0x0a, 0x8e, 0x48, 0xc7, 0x18
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_confounder,
- 0x7b, 0xca, 0x28, 0x5e, 0x2f, 0xd4, 0x13, 0x0f,
- 0xb5, 0x5b, 0x1a, 0x5c, 0x83, 0xbc, 0x5b, 0x24
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_expected_result,
- 0x84, 0xd7, 0xf3, 0x07, 0x54, 0xed, 0x98, 0x7b,
- 0xab, 0x0b, 0xf3, 0x50, 0x6b, 0xeb, 0x09, 0xcf,
- 0xb5, 0x54, 0x02, 0xce, 0xf7, 0xe6
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_expected_hmac,
- 0x87, 0x7c, 0xe9, 0x9e, 0x24, 0x7e, 0x52, 0xd1,
- 0x6e, 0xd4, 0x42, 0x1d, 0xfd, 0xf8, 0x97, 0x6c
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_confounder,
- 0x56, 0xab, 0x21, 0x71, 0x3f, 0xf6, 0x2c, 0x0a,
- 0x14, 0x57, 0x20, 0x0f, 0x6f, 0xa9, 0x94, 0x8f
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_expected_result,
- 0x35, 0x17, 0xd6, 0x40, 0xf5, 0x0d, 0xdc, 0x8a,
- 0xd3, 0x62, 0x87, 0x22, 0xb3, 0x56, 0x9d, 0x2a,
- 0xe0, 0x74, 0x93, 0xfa, 0x82, 0x63, 0x25, 0x40,
- 0x80, 0xea, 0x65, 0xc1, 0x00, 0x8e, 0x8f, 0xc2
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_expected_hmac,
- 0x95, 0xfb, 0x48, 0x52, 0xe7, 0xd8, 0x3e, 0x1e,
- 0x7c, 0x48, 0xc3, 0x7e, 0xeb, 0xe6, 0xb0, 0xd3
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_confounder,
- 0xa7, 0xa4, 0xe2, 0x9a, 0x47, 0x28, 0xce, 0x10,
- 0x66, 0x4f, 0xb6, 0x4e, 0x49, 0xad, 0x3f, 0xac
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_expected_result,
- 0x72, 0x0f, 0x73, 0xb1, 0x8d, 0x98, 0x59, 0xcd,
- 0x6c, 0xcb, 0x43, 0x46, 0x11, 0x5c, 0xd3, 0x36,
- 0xc7, 0x0f, 0x58, 0xed, 0xc0, 0xc4, 0x43, 0x7c,
- 0x55, 0x73, 0x54, 0x4c, 0x31, 0xc8, 0x13, 0xbc,
- 0xe1, 0xe6, 0xd0, 0x72, 0xc1
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_expected_hmac,
- 0x86, 0xb3, 0x9a, 0x41, 0x3c, 0x2f, 0x92, 0xca,
- 0x9b, 0x83, 0x34, 0xa2, 0x87, 0xff, 0xcb, 0xfc
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_confounder,
- 0xf7, 0x64, 0xe9, 0xfa, 0x15, 0xc2, 0x76, 0x47,
- 0x8b, 0x2c, 0x7d, 0x0c, 0x4e, 0x5f, 0x58, 0xe4
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_expected_result,
- 0x41, 0xf5, 0x3f, 0xa5, 0xbf, 0xe7, 0x02, 0x6d,
- 0x91, 0xfa, 0xf9, 0xbe, 0x95, 0x91, 0x95, 0xa0
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_expected_hmac,
- 0x58, 0x70, 0x72, 0x73, 0xa9, 0x6a, 0x40, 0xf0,
- 0xa0, 0x19, 0x60, 0x62, 0x1a, 0xc6, 0x12, 0x74,
- 0x8b, 0x9b, 0xbf, 0xbe, 0x7e, 0xb4, 0xce, 0x3c
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_confounder,
- 0xb8, 0x0d, 0x32, 0x51, 0xc1, 0xf6, 0x47, 0x14,
- 0x94, 0x25, 0x6f, 0xfe, 0x71, 0x2d, 0x0b, 0x9a
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_expected_result,
- 0x4e, 0xd7, 0xb3, 0x7c, 0x2b, 0xca, 0xc8, 0xf7,
- 0x4f, 0x23, 0xc1, 0xcf, 0x07, 0xe6, 0x2b, 0xc7,
- 0xb7, 0x5f, 0xb3, 0xf6, 0x37, 0xb9
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_expected_hmac,
- 0xf5, 0x59, 0xc7, 0xf6, 0x64, 0xf6, 0x9e, 0xab,
- 0x7b, 0x60, 0x92, 0x23, 0x75, 0x26, 0xea, 0x0d,
- 0x1f, 0x61, 0xcb, 0x20, 0xd6, 0x9d, 0x10, 0xf2
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_confounder,
- 0x53, 0xbf, 0x8a, 0x0d, 0x10, 0x52, 0x65, 0xd4,
- 0xe2, 0x76, 0x42, 0x86, 0x24, 0xce, 0x5e, 0x63
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_expected_result,
- 0xbc, 0x47, 0xff, 0xec, 0x79, 0x98, 0xeb, 0x91,
- 0xe8, 0x11, 0x5c, 0xf8, 0xd1, 0x9d, 0xac, 0x4b,
- 0xbb, 0xe2, 0xe1, 0x63, 0xe8, 0x7d, 0xd3, 0x7f,
- 0x49, 0xbe, 0xca, 0x92, 0x02, 0x77, 0x64, 0xf6
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_expected_hmac,
- 0x8c, 0xf5, 0x1f, 0x14, 0xd7, 0x98, 0xc2, 0x27,
- 0x3f, 0x35, 0xdf, 0x57, 0x4d, 0x1f, 0x93, 0x2e,
- 0x40, 0xc4, 0xff, 0x25, 0x5b, 0x36, 0xa2, 0x66
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_confounder,
- 0x76, 0x3e, 0x65, 0x36, 0x7e, 0x86, 0x4f, 0x02,
- 0xf5, 0x51, 0x53, 0xc7, 0xe3, 0xb5, 0x8a, 0xf1
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_expected_result,
- 0x40, 0x01, 0x3e, 0x2d, 0xf5, 0x8e, 0x87, 0x51,
- 0x95, 0x7d, 0x28, 0x78, 0xbc, 0xd2, 0xd6, 0xfe,
- 0x10, 0x1c, 0xcf, 0xd5, 0x56, 0xcb, 0x1e, 0xae,
- 0x79, 0xdb, 0x3c, 0x3e, 0xe8, 0x64, 0x29, 0xf2,
- 0xb2, 0xa6, 0x02, 0xac, 0x86
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_expected_hmac,
- 0xfe, 0xf6, 0xec, 0xb6, 0x47, 0xd6, 0x29, 0x5f,
- 0xae, 0x07, 0x7a, 0x1f, 0xeb, 0x51, 0x75, 0x08,
- 0xd2, 0xc1, 0x6b, 0x41, 0x92, 0xe0, 0x1f, 0x62
-);
-
-static const struct gss_krb5_test_param rfc8009_encrypt_test_params[] = {
- {
- .desc = "Encrypt empty plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_empty_plaintext,
- .confounder = &rfc8009_enc_test1_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test1_expected_result,
- .expected_hmac = &rfc8009_enc_test1_expected_hmac,
- },
- {
- .desc = "Encrypt short plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_short_plaintext,
- .confounder = &rfc8009_enc_test2_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test2_expected_result,
- .expected_hmac = &rfc8009_enc_test2_expected_hmac,
- },
- {
- .desc = "Encrypt block plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_block_plaintext,
- .confounder = &rfc8009_enc_test3_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test3_expected_result,
- .expected_hmac = &rfc8009_enc_test3_expected_hmac,
- },
- {
- .desc = "Encrypt long plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_long_plaintext,
- .confounder = &rfc8009_enc_test4_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test4_expected_result,
- .expected_hmac = &rfc8009_enc_test4_expected_hmac,
- },
- {
- .desc = "Encrypt empty plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_empty_plaintext,
- .confounder = &rfc8009_enc_test5_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test5_expected_result,
- .expected_hmac = &rfc8009_enc_test5_expected_hmac,
- },
- {
- .desc = "Encrypt short plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_short_plaintext,
- .confounder = &rfc8009_enc_test6_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test6_expected_result,
- .expected_hmac = &rfc8009_enc_test6_expected_hmac,
- },
- {
- .desc = "Encrypt block plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_block_plaintext,
- .confounder = &rfc8009_enc_test7_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test7_expected_result,
- .expected_hmac = &rfc8009_enc_test7_expected_hmac,
- },
- {
- .desc = "Encrypt long plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_long_plaintext,
- .confounder = &rfc8009_enc_test8_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test8_expected_result,
- .expected_hmac = &rfc8009_enc_test8_expected_hmac,
- },
-};
-
-/* Creates the function rfc8009_encrypt_gen_params */
-KUNIT_ARRAY_PARAM(rfc8009_encrypt, rfc8009_encrypt_test_params,
- gss_krb5_get_desc);
-
-static void rfc8009_encrypt_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj Ke, Ki, checksum;
- u8 usage_data[GSS_KRB5_K5CLENGTH];
- struct xdr_netobj usage = {
- .data = usage_data,
- .len = sizeof(usage_data),
- };
- struct crypto_ahash *ahash_tfm;
- struct xdr_buf buf;
- void *text;
- size_t len;
- u32 err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- *(__be32 *)usage.data = cpu_to_be32(2);
-
- Ke.len = gk5e->Ke_length;
- Ke.data = kunit_kzalloc(test, Ke.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ke.data);
- usage.data[4] = KEY_USAGE_SEED_ENCRYPTION;
- err = gk5e->derive_key(gk5e, param->base_key, &Ke,
- &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- len = param->confounder->len + param->plaintext->len;
- text = kunit_kzalloc(test, len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
- memcpy(text, param->confounder->data, param->confounder->len);
- memcpy(text + param->confounder->len, param->plaintext->data,
- param->plaintext->len);
-
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->confounder->len + param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- checksum.len = gk5e->cksumlength;
- checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data);
-
- Ki.len = gk5e->Ki_length;
- Ki.data = kunit_kzalloc(test, Ki.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ki.data);
- usage.data[4] = KEY_USAGE_SEED_INTEGRITY;
- err = gk5e->derive_key(gk5e, param->base_key, &Ki,
- &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- ahash_tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ahash_tfm);
- err = crypto_ahash_setkey(ahash_tfm, Ki.data, Ki.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Act */
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0);
- KUNIT_ASSERT_EQ(test, err, 0);
- err = krb5_etm_checksum(cts_tfm, ahash_tfm, &buf, 0, &checksum);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test,
- param->expected_result->len, buf.len,
- "ciphertext length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- buf.head[0].iov_base,
- param->expected_result->len,
- "ciphertext mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_hmac->data,
- checksum.data,
- checksum.len,
- "HMAC mismatch");
-
- crypto_free_ahash(ahash_tfm);
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case rfc8009_test_cases[] = {
- {
- .name = "RFC 8009 key derivation",
- .run_case = kdf_case,
- .generate_params = rfc8009_kdf_gen_params,
- },
- {
- .name = "RFC 8009 checksum",
- .run_case = checksum_case,
- .generate_params = rfc8009_checksum_gen_params,
- },
- {
- .name = "RFC 8009 encryption",
- .run_case = rfc8009_encrypt_case,
- .generate_params = rfc8009_encrypt_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc8009_suite = {
- .name = "RFC 8009 suite",
- .test_cases = rfc8009_test_cases,
-};
-
-/*
- * Encryption self-tests
- */
-
-DEFINE_STR_XDR_NETOBJ(encrypt_selftest_plaintext,
- "This is the plaintext for the encryption self-test.");
-
-static const struct gss_krb5_test_param encrypt_selftest_params[] = {
- {
- .desc = "aes128-cts-hmac-sha1-96 encryption self-test",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "aes256-cts-hmac-sha1-96 encryption self-test",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "camellia128-cts-cmac encryption self-test",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .Ke = &camellia128_cts_cmac_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "camellia256-cts-cmac encryption self-test",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .Ke = &camellia256_cts_cmac_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "aes128-cts-hmac-sha256-128 encryption self-test",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .Ke = &aes128_cts_hmac_sha256_128_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "aes256-cts-hmac-sha384-192 encryption self-test",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .Ke = &aes256_cts_hmac_sha384_192_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
-};
-
-/* Creates the function encrypt_selftest_gen_params */
-KUNIT_ARRAY_PARAM(encrypt_selftest, encrypt_selftest_params,
- gss_krb5_get_desc);
-
-/*
- * Encrypt and decrypt plaintext, and ensure the input plaintext
- * matches the output plaintext. A confounder is not added in this
- * case.
- */
-static void encrypt_selftest_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_buf buf;
- void *text;
- int err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- text = kunit_kzalloc(test, roundup(param->plaintext->len,
- crypto_sync_skcipher_blocksize(cbc_tfm)),
- GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
-
- memcpy(text, param->plaintext->data, param->plaintext->len);
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- /* Act */
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0);
- KUNIT_ASSERT_EQ(test, err, 0);
- err = krb5_cbc_cts_decrypt(cts_tfm, cbc_tfm, 0, &buf);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test,
- param->plaintext->len, buf.len,
- "length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->plaintext->data,
- buf.head[0].iov_base,
- buf.len,
- "plaintext mismatch");
-
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case encryption_test_cases[] = {
- {
- .name = "Encryption self-tests",
- .run_case = encrypt_selftest_case,
- .generate_params = encrypt_selftest_gen_params,
- },
- {}
-};
-
-static struct kunit_suite encryption_test_suite = {
- .name = "Encryption test suite",
- .test_cases = encryption_test_cases,
-};
-
-kunit_test_suites(&rfc3961_suite,
- &rfc3962_suite,
- &rfc6803_suite,
- &rfc8009_suite,
- &encryption_test_suite);
-
-MODULE_DESCRIPTION("Test RPCSEC GSS Kerberos 5 functions");
-MODULE_LICENSE("GPL");
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c
index ef0e6af9fc95..b5fb70419faa 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -60,6 +60,8 @@
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include "gss_krb5_internal.h"
@@ -71,18 +73,19 @@ u32
gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
struct xdr_netobj *read_token)
{
- struct crypto_ahash *tfm = ctx->initiate ?
- ctx->acceptor_sign : ctx->initiator_sign;
- char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
- struct xdr_netobj cksumobj = {
- .len = ctx->gk5e->cksumlength,
- .data = cksumdata,
- };
+ const struct krb5_enctype *krb5 = ctx->krb5e;
+ struct crypto_shash *shash = ctx->initiate ?
+ ctx->acceptor_sign_shash : ctx->initiator_sign_shash;
+ unsigned int cksum_len = krb5->cksum_len;
+ struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow;
+ size_t mic_offset, mic_len;
u8 *ptr = read_token->data;
__be16 be16_ptr;
time64_t now;
u8 flags;
- int i;
+ int nsg, i;
+ int ret;
dprintk("RPC: %s\n", __func__);
@@ -104,13 +107,20 @@ gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
if (ptr[i] != 0xff)
return GSS_S_DEFECTIVE_TOKEN;
- if (gss_krb5_checksum(tfm, ptr, GSS_KRB5_TOK_HDR_LEN,
- message_buffer, 0, &cksumobj))
+ nsg = gss_krb5_mic_build_sg(message_buffer,
+ ptr + GSS_KRB5_TOK_HDR_LEN,
+ cksum_len, ptr,
+ sg_head, &sg_overflow);
+ if (nsg < 0)
return GSS_S_FAILURE;
- if (memcmp(cksumobj.data, ptr + GSS_KRB5_TOK_HDR_LEN,
- ctx->gk5e->cksumlength))
- return GSS_S_BAD_SIG;
+ mic_offset = 0;
+ mic_len = cksum_len + message_buffer->len + GSS_KRB5_TOK_HDR_LEN;
+ ret = crypto_krb5_verify_mic(krb5, shash, NULL, sg_head, nsg,
+ &mic_offset, &mic_len);
+ kfree(sg_overflow);
+ if (ret)
+ return gss_krb5_errno_to_status(ret);
/* it got through unscathed. Make sure the context is unexpired */
now = ktime_get_real_seconds();
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index b3e1738ff6bf..ac4b32df42b9 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -28,7 +28,6 @@
* SUCH DAMAGES.
*/
-#include <crypto/skcipher.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
@@ -112,9 +111,9 @@ gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
*ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
*ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
- if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
+ if (!kctx->initiate)
flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
- if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0)
+ if (kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
/* We always do confidentiality in wrap tokens */
flags |= KG2_TOKEN_FLAG_SEALED;
@@ -130,7 +129,7 @@ gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
be64ptr = (__be64 *)be16ptr;
*be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64));
- err = (*kctx->gk5e->encrypt)(kctx, offset, buf, pages);
+ err = gss_krb5_aead_encrypt(kctx, offset, buf, pages);
if (err)
return err;
@@ -184,10 +183,10 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
if (rrc != 0)
rotate_left(offset + 16, buf, rrc);
- err = (*kctx->gk5e->decrypt)(kctx, offset, len, buf,
- &headskip, &tailskip);
+ err = gss_krb5_aead_decrypt(kctx, offset, len, buf,
+ &headskip, &tailskip);
if (err)
- return GSS_S_FAILURE;
+ return err;
/*
* Retrieve the decrypted gss token header and verify
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 161d02cc1c2c..d14209031e18 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -206,7 +206,7 @@ static struct cache_head *rsi_alloc(void)
static int rsi_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall_timeout(cd, h);
+ return sunrpc_cache_upcall_warn(cd, h);
}
static void rsi_request(struct cache_detail *cd,
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 27dd6b58b8ff..391037f15292 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -33,9 +33,11 @@
#include <linux/sunrpc/cache.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
+#include <net/genetlink.h>
#include <trace/events/sunrpc.h>
#include "netns.h"
+#include "netlink.h"
#include "fail.h"
#define RPCDBG_FACILITY RPCDBG_CACHE
@@ -1195,12 +1197,12 @@ static bool cache_listeners_exist(struct cache_detail *detail)
}
/*
- * register an upcall request to user-space and queue it up for read() by the
- * upcall daemon.
+ * register an upcall request to user-space and queue it up to be fetched by
+ * the upcall daemon.
*
* Each request is at most one page long.
*/
-static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
+static int cache_do_upcall(struct cache_detail *detail, struct cache_head *h)
{
char *buf;
struct cache_request *crq;
@@ -1233,6 +1235,8 @@ static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
/* Lost a race, no longer PENDING, so don't enqueue */
ret = -EAGAIN;
spin_unlock(&detail->queue_lock);
+ if (ret != -EAGAIN && detail->cache_notify)
+ detail->cache_notify(detail, h);
wake_up(&detail->queue_wait);
if (ret == -EAGAIN) {
kfree(buf);
@@ -1241,25 +1245,25 @@ static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
return ret;
}
-int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
+int sunrpc_cache_upcall(struct cache_detail *detail, struct cache_head *h)
{
if (test_and_set_bit(CACHE_PENDING, &h->flags))
return 0;
- return cache_pipe_upcall(detail, h);
+ return cache_do_upcall(detail, h);
}
-EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
+EXPORT_SYMBOL_GPL(sunrpc_cache_upcall);
-int sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail,
- struct cache_head *h)
+int sunrpc_cache_upcall_warn(struct cache_detail *detail,
+ struct cache_head *h)
{
if (!cache_listeners_exist(detail)) {
warn_no_listener(detail);
trace_cache_entry_no_listener(detail, h);
return -EINVAL;
}
- return sunrpc_cache_pipe_upcall(detail, h);
+ return sunrpc_cache_upcall(detail, h);
}
-EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall_timeout);
+EXPORT_SYMBOL_GPL(sunrpc_cache_upcall_warn);
/*
* parse a message from user-space and pass it
@@ -1900,3 +1904,106 @@ void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
spin_unlock(&cd->hash_lock);
}
EXPORT_SYMBOL_GPL(sunrpc_cache_unhash);
+
+/**
+ * sunrpc_cache_requests_count - count pending upcall requests
+ * @cd: cache_detail to query
+ *
+ * Returns the number of requests on the cache's request list that
+ * still have CACHE_PENDING set.
+ */
+int sunrpc_cache_requests_count(struct cache_detail *cd)
+{
+ struct cache_request *crq;
+ int cnt = 0;
+
+ spin_lock(&cd->queue_lock);
+ list_for_each_entry(crq, &cd->requests, list) {
+ if (test_bit(CACHE_PENDING, &crq->item->flags))
+ cnt++;
+ }
+ spin_unlock(&cd->queue_lock);
+ return cnt;
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_requests_count);
+
+/**
+ * sunrpc_cache_requests_snapshot - snapshot pending upcall requests
+ * @cd: cache_detail to query
+ * @items: array to fill with cache_head pointers (caller-allocated)
+ * @seqnos: array to fill with sequence numbers (caller-allocated)
+ * @max: size of the arrays
+ * @min_seqno: only include entries with seqno > min_seqno (0 for all)
+ *
+ * Only entries with CACHE_PENDING set are included. Takes a reference
+ * on each cache_head via cache_get(). Caller must call cache_put()
+ * on each returned item when done.
+ *
+ * Returns the number of entries filled.
+ */
+int sunrpc_cache_requests_snapshot(struct cache_detail *cd,
+ struct cache_head **items,
+ u64 *seqnos, int max,
+ u64 min_seqno)
+{
+ struct cache_request *crq;
+ int i = 0;
+
+ spin_lock(&cd->queue_lock);
+ list_for_each_entry(crq, &cd->requests, list) {
+ if (i >= max)
+ break;
+ if (!test_bit(CACHE_PENDING, &crq->item->flags))
+ continue;
+ if (crq->seqno <= min_seqno)
+ continue;
+ items[i] = cache_get(crq->item);
+ seqnos[i] = crq->seqno;
+ i++;
+ }
+ spin_unlock(&cd->queue_lock);
+ return i;
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_requests_snapshot);
+
+/**
+ * sunrpc_cache_notify - send a netlink notification for a cache event
+ * @cd: cache_detail for the cache
+ * @h: cache_head entry (unused, reserved for future use)
+ * @cache_type: cache type identifier (e.g. SUNRPC_CACHE_TYPE_UNIX_GID)
+ *
+ * Sends a SUNRPC_CMD_CACHE_NOTIFY multicast message on the "exportd"
+ * group if any listeners are present. Returns 0 on success or a
+ * negative errno.
+ */
+int sunrpc_cache_notify(struct cache_detail *cd, struct cache_head *h,
+ u32 cache_type)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *msg;
+
+ if (!genl_has_listeners(&sunrpc_nl_family, cd->net,
+ SUNRPC_NLGRP_EXPORTD))
+ return -ENOLINK;
+
+ msg = genlmsg_new(nla_total_size(sizeof(u32)), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &sunrpc_nl_family, 0,
+ SUNRPC_CMD_CACHE_NOTIFY);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(msg, SUNRPC_A_CACHE_NOTIFY_CACHE_TYPE, cache_type)) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_multicast_netns(&sunrpc_nl_family, cd->net, msg, 0,
+ SUNRPC_NLGRP_EXPORTD, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_notify);
diff --git a/net/sunrpc/netlink.c b/net/sunrpc/netlink.c
new file mode 100644
index 000000000000..ce09ecc0faa2
--- /dev/null
+++ b/net/sunrpc/netlink.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/sunrpc_cache.yaml */
+/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "netlink.h"
+
+#include <uapi/linux/sunrpc_netlink.h>
+
+/* Common nested types */
+const struct nla_policy sunrpc_ip_map_nl_policy[SUNRPC_A_IP_MAP_EXPIRY + 1] = {
+ [SUNRPC_A_IP_MAP_SEQNO] = { .type = NLA_U64, },
+ [SUNRPC_A_IP_MAP_CLASS] = { .type = NLA_NUL_STRING, },
+ [SUNRPC_A_IP_MAP_ADDR] = { .type = NLA_NUL_STRING, },
+ [SUNRPC_A_IP_MAP_DOMAIN] = { .type = NLA_NUL_STRING, },
+ [SUNRPC_A_IP_MAP_NEGATIVE] = { .type = NLA_FLAG, },
+ [SUNRPC_A_IP_MAP_EXPIRY] = { .type = NLA_U64, },
+};
+
+const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1] = {
+ [SUNRPC_A_UNIX_GID_SEQNO] = { .type = NLA_U64, },
+ [SUNRPC_A_UNIX_GID_UID] = { .type = NLA_U32, },
+ [SUNRPC_A_UNIX_GID_GIDS] = { .type = NLA_U32, },
+ [SUNRPC_A_UNIX_GID_NEGATIVE] = { .type = NLA_FLAG, },
+ [SUNRPC_A_UNIX_GID_EXPIRY] = { .type = NLA_U64, },
+};
+
+/* SUNRPC_CMD_IP_MAP_SET_REQS - do */
+static const struct nla_policy sunrpc_ip_map_set_reqs_nl_policy[SUNRPC_A_IP_MAP_REQS_REQUESTS + 1] = {
+ [SUNRPC_A_IP_MAP_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_ip_map_nl_policy),
+};
+
+/* SUNRPC_CMD_UNIX_GID_SET_REQS - do */
+static const struct nla_policy sunrpc_unix_gid_set_reqs_nl_policy[SUNRPC_A_UNIX_GID_REQS_REQUESTS + 1] = {
+ [SUNRPC_A_UNIX_GID_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_unix_gid_nl_policy),
+};
+
+/* SUNRPC_CMD_CACHE_FLUSH - do */
+static const struct nla_policy sunrpc_cache_flush_nl_policy[SUNRPC_A_CACHE_FLUSH_MASK + 1] = {
+ [SUNRPC_A_CACHE_FLUSH_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3),
+};
+
+/* Ops table for sunrpc */
+static const struct genl_split_ops sunrpc_nl_ops[] = {
+ {
+ .cmd = SUNRPC_CMD_IP_MAP_GET_REQS,
+ .dumpit = sunrpc_nl_ip_map_get_reqs_dumpit,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = SUNRPC_CMD_IP_MAP_SET_REQS,
+ .doit = sunrpc_nl_ip_map_set_reqs_doit,
+ .policy = sunrpc_ip_map_set_reqs_nl_policy,
+ .maxattr = SUNRPC_A_IP_MAP_REQS_REQUESTS,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = SUNRPC_CMD_UNIX_GID_GET_REQS,
+ .dumpit = sunrpc_nl_unix_gid_get_reqs_dumpit,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = SUNRPC_CMD_UNIX_GID_SET_REQS,
+ .doit = sunrpc_nl_unix_gid_set_reqs_doit,
+ .policy = sunrpc_unix_gid_set_reqs_nl_policy,
+ .maxattr = SUNRPC_A_UNIX_GID_REQS_REQUESTS,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = SUNRPC_CMD_CACHE_FLUSH,
+ .doit = sunrpc_nl_cache_flush_doit,
+ .policy = sunrpc_cache_flush_nl_policy,
+ .maxattr = SUNRPC_A_CACHE_FLUSH_MASK,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+};
+
+static const struct genl_multicast_group sunrpc_nl_mcgrps[] = {
+ [SUNRPC_NLGRP_NONE] = { "none", },
+ [SUNRPC_NLGRP_EXPORTD] = { "exportd", },
+};
+
+struct genl_family sunrpc_nl_family __ro_after_init = {
+ .name = SUNRPC_FAMILY_NAME,
+ .version = SUNRPC_FAMILY_VERSION,
+ .netnsok = true,
+ .parallel_ops = true,
+ .module = THIS_MODULE,
+ .split_ops = sunrpc_nl_ops,
+ .n_split_ops = ARRAY_SIZE(sunrpc_nl_ops),
+ .mcgrps = sunrpc_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(sunrpc_nl_mcgrps),
+};
diff --git a/net/sunrpc/netlink.h b/net/sunrpc/netlink.h
new file mode 100644
index 000000000000..2c1012303d48
--- /dev/null
+++ b/net/sunrpc/netlink.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/sunrpc_cache.yaml */
+/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _LINUX_SUNRPC_GEN_H
+#define _LINUX_SUNRPC_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/sunrpc_netlink.h>
+
+/* Common nested types */
+extern const struct nla_policy sunrpc_ip_map_nl_policy[SUNRPC_A_IP_MAP_EXPIRY + 1];
+extern const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1];
+
+int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb);
+int sunrpc_nl_ip_map_set_reqs_doit(struct sk_buff *skb, struct genl_info *info);
+int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb);
+int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info);
+int sunrpc_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info);
+
+enum {
+ SUNRPC_NLGRP_NONE,
+ SUNRPC_NLGRP_EXPORTD,
+};
+
+extern struct genl_family sunrpc_nl_family;
+
+#endif /* _LINUX_SUNRPC_GEN_H */
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index bab6cab29405..ab88ce46afb5 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -23,9 +23,12 @@
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/sunrpc/xprtsock.h>
+#include <net/genetlink.h>
+
#include "sunrpc.h"
#include "sysfs.h"
#include "netns.h"
+#include "netlink.h"
unsigned int sunrpc_net_id;
EXPORT_SYMBOL_GPL(sunrpc_net_id);
@@ -108,6 +111,10 @@ init_sunrpc(void)
if (err)
goto out5;
+ err = genl_register_family(&sunrpc_nl_family);
+ if (err)
+ goto out6;
+
sunrpc_debugfs_init();
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
rpc_register_sysctl();
@@ -116,6 +123,8 @@ init_sunrpc(void)
init_socket_xprt(); /* clnt sock transport */
return 0;
+out6:
+ rpc_sysfs_exit();
out5:
unregister_rpc_pipefs();
out4:
@@ -131,6 +140,7 @@ out:
static void __exit
cleanup_sunrpc(void)
{
+ genl_unregister_family(&sunrpc_nl_family);
rpc_sysfs_exit();
rpc_cleanup_clids();
xprt_cleanup_ids();
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 576fa42e7abf..ae9ec4bf34f7 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1598,6 +1598,12 @@ static void svc_release_rqst(struct svc_rqst *rqstp)
if (procp && procp->pc_release)
procp->pc_release(rqstp);
+
+ /*
+ * A subsequent svc_release_rqst() on this rqstp must not
+ * re-invoke pc_release against released state.
+ */
+ rqstp->rq_procinfo = NULL;
}
/**
@@ -1616,6 +1622,9 @@ void svc_process(struct svc_rqst *rqstp)
svc_xprt_deferred_close(rqstp->rq_xprt);
#endif
+ /* Discard a stale release hook from a previous RPC. */
+ rqstp->rq_procinfo = NULL;
+
/*
* Setup response xdr_buf.
* Initially it has just one page
@@ -1672,6 +1681,7 @@ void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp)
int proc_error;
/* Build the svc_rqst used by the common processing routine */
+ rqstp->rq_procinfo = NULL;
rqstp->rq_xid = req->rq_xid;
rqstp->rq_prot = req->rq_xprt->prot;
rqstp->rq_bc_net = req->rq_xprt->xprt_net;
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index b16e710926c1..63d1002e63e7 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -234,7 +234,19 @@ void svc_xprt_received(struct svc_xprt *xprt)
svc_xprt_get(xprt);
smp_mb__before_atomic();
clear_bit(XPT_BUSY, &xprt->xpt_flags);
- svc_xprt_enqueue(xprt);
+
+ /*
+ * Skip the enqueue when no actionable flags are set.
+ * Each producer both sets its flag (XPT_DATA, XPT_CLOSE,
+ * etc.) and calls svc_xprt_enqueue(); if a set_bit races
+ * with this check, the producer's own enqueue observes
+ * !XPT_BUSY and dispatches the transport.
+ */
+ if (READ_ONCE(xprt->xpt_flags) &
+ (BIT(XPT_CONN) | BIT(XPT_CLOSE) | BIT(XPT_HANDSHAKE) |
+ BIT(XPT_DATA) | BIT(XPT_DEFERRED)))
+ svc_xprt_enqueue(xprt);
+
svc_xprt_put(xprt);
}
EXPORT_SYMBOL_GPL(svc_xprt_received);
@@ -425,13 +437,35 @@ static bool svc_xprt_reserve_slot(struct svc_rqst *rqstp, struct svc_xprt *xprt)
return true;
}
+/*
+ * After a caller releases write-space or a request slot,
+ * re-enqueue the transport only when there is pending
+ * work that a thread could act on. The smp_mb() pairs
+ * with the smp_rmb() in svc_xprt_ready() and orders the
+ * preceding counter update before the flags read so a
+ * concurrent set_bit(XPT_DATA) is visible here.
+ *
+ * When the transport is BUSY, the thread holding it will
+ * call svc_xprt_received() upon completion, which checks
+ * for pending work and re-enqueues as needed.
+ */
+static void svc_xprt_resource_released(struct svc_xprt *xprt)
+{
+ unsigned long xpt_flags;
+
+ smp_mb();
+ xpt_flags = READ_ONCE(xprt->xpt_flags);
+ if (xpt_flags & (BIT(XPT_DATA) | BIT(XPT_DEFERRED)) &&
+ !(xpt_flags & BIT(XPT_BUSY)))
+ svc_xprt_enqueue(xprt);
+}
+
static void svc_xprt_release_slot(struct svc_rqst *rqstp)
{
struct svc_xprt *xprt = rqstp->rq_xprt;
if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) {
atomic_dec(&xprt->xpt_nr_rqsts);
- smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */
- svc_xprt_enqueue(xprt);
+ svc_xprt_resource_released(xprt);
}
}
@@ -525,10 +559,10 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
space += rqstp->rq_res.head[0].iov_len;
if (xprt && space < rqstp->rq_reserved) {
- atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved);
+ atomic_sub((rqstp->rq_reserved - space),
+ &xprt->xpt_reserved);
rqstp->rq_reserved = space;
- smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */
- svc_xprt_enqueue(xprt);
+ svc_xprt_resource_released(xprt);
}
}
EXPORT_SYMBOL_GPL(svc_reserve);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 3be69c145d2a..64a2658faddb 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -17,11 +17,14 @@
#include <net/ipv6.h>
#include <linux/kernel.h>
#include <linux/user_namespace.h>
+#include <net/genetlink.h>
+#include <uapi/linux/sunrpc_netlink.h>
#include <trace/events/sunrpc.h>
#define RPCDBG_FACILITY RPCDBG_AUTH
#include "netns.h"
+#include "netlink.h"
/*
* AUTHUNIX and AUTHNULL credentials are both handled here.
@@ -152,7 +155,7 @@ static struct cache_head *ip_map_alloc(void)
static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall(cd, h);
+ return sunrpc_cache_upcall(cd, h);
}
static void ip_map_request(struct cache_detail *cd,
@@ -467,7 +470,7 @@ static struct cache_head *unix_gid_alloc(void)
static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall_timeout(cd, h);
+ return sunrpc_cache_upcall_warn(cd, h);
}
static void unix_gid_request(struct cache_detail *cd,
@@ -582,12 +585,278 @@ static int unix_gid_show(struct seq_file *m,
return 0;
}
+static int unix_gid_notify(struct cache_detail *cd, struct cache_head *h)
+{
+ return sunrpc_cache_notify(cd, h, SUNRPC_CACHE_TYPE_UNIX_GID);
+}
+
+/**
+ * sunrpc_nl_unix_gid_get_reqs_dumpit - dump pending unix_gid requests
+ * @skb: reply buffer
+ * @cb: netlink metadata and command arguments
+ *
+ * Walk the unix_gid cache's pending request list and create a netlink
+ * message with a nested entry for each cache_request, containing the
+ * seqno and uid.
+ *
+ * Uses cb->args[0] as a seqno cursor for dump continuation across
+ * multiple netlink messages.
+ *
+ * Returns the size of the reply or a negative errno.
+ */
+int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ struct cache_head **items;
+ u64 *seqnos;
+ int cnt, i, emitted;
+ void *hdr;
+ int ret;
+
+ sn = net_generic(sock_net(skb->sk), sunrpc_net_id);
+
+ cd = sn->unix_gid_cache;
+ if (!cd)
+ return -ENODEV;
+
+ cnt = sunrpc_cache_requests_count(cd);
+ if (!cnt)
+ return 0;
+
+ items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
+ if (!items || !seqnos) {
+ ret = -ENOMEM;
+ goto out_alloc;
+ }
+
+ cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt,
+ cb->args[0]);
+ if (!cnt) {
+ ret = 0;
+ goto out_alloc;
+ }
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &sunrpc_nl_family,
+ NLM_F_MULTI, SUNRPC_CMD_UNIX_GID_GET_REQS);
+ if (!hdr) {
+ ret = -ENOBUFS;
+ goto out_put;
+ }
+
+ emitted = 0;
+ for (i = 0; i < cnt; i++) {
+ struct unix_gid *ug;
+ struct nlattr *nest;
+
+ ug = container_of(items[i], struct unix_gid, h);
+
+ nest = nla_nest_start(skb,
+ SUNRPC_A_UNIX_GID_REQS_REQUESTS);
+ if (!nest)
+ break;
+
+ if (nla_put_u64_64bit(skb, SUNRPC_A_UNIX_GID_SEQNO,
+ seqnos[i], 0) ||
+ nla_put_u32(skb, SUNRPC_A_UNIX_GID_UID,
+ from_kuid(&init_user_ns, ug->uid))) {
+ nla_nest_cancel(skb, nest);
+ break;
+ }
+
+ nla_nest_end(skb, nest);
+ cb->args[0] = seqnos[i];
+ emitted++;
+ }
+
+ if (!emitted) {
+ genlmsg_cancel(skb, hdr);
+ ret = -EMSGSIZE;
+ goto out_put;
+ }
+
+ genlmsg_end(skb, hdr);
+ ret = skb->len;
+out_put:
+ for (i = 0; i < cnt; i++)
+ cache_put(items[i], cd);
+out_alloc:
+ kfree(seqnos);
+ kfree(items);
+ return ret;
+}
+
+/**
+ * sunrpc_nl_parse_one_unix_gid - parse one unix_gid entry from netlink
+ * @cd: cache_detail for the unix_gid cache
+ * @attr: nested attribute containing unix_gid fields
+ *
+ * Parses one unix_gid entry from a netlink message and updates the
+ * cache. Mirrors the logic in unix_gid_parse().
+ *
+ * Returns 0 on success or a negative errno.
+ */
+static int sunrpc_nl_parse_one_unix_gid(struct cache_detail *cd,
+ struct nlattr *attr)
+{
+ struct nlattr *tb[SUNRPC_A_UNIX_GID_EXPIRY + 1];
+ struct unix_gid ug, *ugp;
+ struct timespec64 boot;
+ struct nlattr *gid_attr;
+ int err, rem, gids = 0;
+ kuid_t uid;
+
+ err = nla_parse_nested(tb, SUNRPC_A_UNIX_GID_EXPIRY, attr,
+ sunrpc_unix_gid_nl_policy, NULL);
+ if (err)
+ return err;
+
+ /* uid (required) */
+ if (!tb[SUNRPC_A_UNIX_GID_UID])
+ return -EINVAL;
+ uid = make_kuid(current_user_ns(),
+ nla_get_u32(tb[SUNRPC_A_UNIX_GID_UID]));
+ ug.uid = uid;
+
+ /* expiry (required, wallclock seconds) */
+ if (!tb[SUNRPC_A_UNIX_GID_EXPIRY])
+ return -EINVAL;
+ getboottime64(&boot);
+ ug.h.flags = 0;
+ ug.h.expiry_time = nla_get_u64(tb[SUNRPC_A_UNIX_GID_EXPIRY]) -
+ boot.tv_sec;
+
+ if (tb[SUNRPC_A_UNIX_GID_NEGATIVE]) {
+ ug.gi = groups_alloc(0);
+ if (!ug.gi)
+ return -ENOMEM;
+ } else {
+ /* Count gids */
+ nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS,
+ attr, rem)
+ gids++;
+
+ if (gids > 8192)
+ return -EINVAL;
+
+ ug.gi = groups_alloc(gids);
+ if (!ug.gi)
+ return -ENOMEM;
+
+ gids = 0;
+ nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS,
+ attr, rem) {
+ kgid_t kgid;
+
+ kgid = make_kgid(current_user_ns(),
+ nla_get_u32(gid_attr));
+ if (!gid_valid(kgid)) {
+ err = -EINVAL;
+ goto out;
+ }
+ ug.gi->gid[gids++] = kgid;
+ }
+ groups_sort(ug.gi);
+ }
+
+ ugp = unix_gid_lookup(cd, uid);
+ if (ugp) {
+ struct cache_head *ch;
+
+ ch = sunrpc_cache_update(cd, &ug.h, &ugp->h,
+ unix_gid_hash(uid));
+ if (!ch) {
+ err = -ENOMEM;
+ } else {
+ err = 0;
+ cache_put(ch, cd);
+ }
+ } else {
+ err = -ENOMEM;
+ }
+out:
+ if (ug.gi)
+ put_group_info(ug.gi);
+ return err;
+}
+
+/**
+ * sunrpc_nl_unix_gid_set_reqs_doit - respond to unix_gid requests
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Parse one or more unix_gid cache responses from userspace and
+ * update the unix_gid cache accordingly.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ const struct nlattr *attr;
+ int rem, ret = 0;
+
+ sn = net_generic(genl_info_net(info), sunrpc_net_id);
+
+ cd = sn->unix_gid_cache;
+ if (!cd)
+ return -ENODEV;
+
+ nlmsg_for_each_attr_type(attr, SUNRPC_A_UNIX_GID_REQS_REQUESTS,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ ret = sunrpc_nl_parse_one_unix_gid(cd,
+ (struct nlattr *)attr);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * sunrpc_nl_cache_flush_doit - flush sunrpc caches via netlink
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Flush the ip_map and/or unix_gid caches. If SUNRPC_A_CACHE_FLUSH_MASK
+ * is provided, only flush the caches indicated by the bitmask (bit 1 =
+ * ip_map, bit 2 = unix_gid). If omitted, flush both.
+ *
+ * Return 0 on success or a negative errno.
+ */
+int sunrpc_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sunrpc_net *sn;
+ u32 mask = ~0U;
+
+ sn = net_generic(genl_info_net(info), sunrpc_net_id);
+
+ if (info->attrs[SUNRPC_A_CACHE_FLUSH_MASK])
+ mask = nla_get_u32(info->attrs[SUNRPC_A_CACHE_FLUSH_MASK]);
+
+ if ((mask & SUNRPC_CACHE_TYPE_IP_MAP) &&
+ sn->ip_map_cache)
+ cache_purge(sn->ip_map_cache);
+
+ if ((mask & SUNRPC_CACHE_TYPE_UNIX_GID) &&
+ sn->unix_gid_cache)
+ cache_purge(sn->unix_gid_cache);
+
+ return 0;
+}
+
static const struct cache_detail unix_gid_cache_template = {
.owner = THIS_MODULE,
.hash_size = GID_HASHMAX,
.name = "auth.unix.gid",
.cache_put = unix_gid_put,
.cache_upcall = unix_gid_upcall,
+ .cache_notify = unix_gid_notify,
.cache_request = unix_gid_request,
.cache_parse = unix_gid_parse,
.cache_show = unix_gid_show,
@@ -1017,12 +1286,255 @@ struct auth_ops svcauth_unix = {
.set_client = svcauth_unix_set_client,
};
+static int ip_map_notify(struct cache_detail *cd, struct cache_head *h)
+{
+ return sunrpc_cache_notify(cd, h, SUNRPC_CACHE_TYPE_IP_MAP);
+}
+
+/**
+ * sunrpc_nl_ip_map_get_reqs_dumpit - dump pending ip_map requests
+ * @skb: reply buffer
+ * @cb: netlink metadata and command arguments
+ *
+ * Walk the ip_map cache's pending request list and create a netlink
+ * message with a nested entry for each cache_request, containing the
+ * seqno, class and addr.
+ *
+ * Uses cb->args[0] as a seqno cursor for dump continuation across
+ * multiple netlink messages.
+ *
+ * Returns the size of the reply or a negative errno.
+ */
+int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ struct cache_head **items;
+ u64 *seqnos;
+ int cnt, i, emitted;
+ void *hdr;
+ int ret;
+
+ sn = net_generic(sock_net(skb->sk), sunrpc_net_id);
+
+ cd = sn->ip_map_cache;
+ if (!cd)
+ return -ENODEV;
+
+ cnt = sunrpc_cache_requests_count(cd);
+ if (!cnt)
+ return 0;
+
+ items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
+ if (!items || !seqnos) {
+ ret = -ENOMEM;
+ goto out_alloc;
+ }
+
+ cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt,
+ cb->args[0]);
+ if (!cnt) {
+ ret = 0;
+ goto out_alloc;
+ }
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &sunrpc_nl_family,
+ NLM_F_MULTI, SUNRPC_CMD_IP_MAP_GET_REQS);
+ if (!hdr) {
+ ret = -ENOBUFS;
+ goto out_put;
+ }
+
+ emitted = 0;
+ for (i = 0; i < cnt; i++) {
+ struct ip_map *im;
+ struct nlattr *nest;
+ char text_addr[40];
+
+ im = container_of(items[i], struct ip_map, h);
+
+ if (ipv6_addr_v4mapped(&im->m_addr))
+ snprintf(text_addr, 20, "%pI4",
+ &im->m_addr.s6_addr32[3]);
+ else
+ snprintf(text_addr, 40, "%pI6", &im->m_addr);
+
+ nest = nla_nest_start(skb, SUNRPC_A_IP_MAP_REQS_REQUESTS);
+ if (!nest)
+ break;
+
+ if (nla_put_u64_64bit(skb, SUNRPC_A_IP_MAP_SEQNO,
+ seqnos[i], 0) ||
+ nla_put_string(skb, SUNRPC_A_IP_MAP_CLASS,
+ im->m_class) ||
+ nla_put_string(skb, SUNRPC_A_IP_MAP_ADDR, text_addr)) {
+ nla_nest_cancel(skb, nest);
+ break;
+ }
+
+ nla_nest_end(skb, nest);
+ cb->args[0] = seqnos[i];
+ emitted++;
+ }
+
+ if (!emitted) {
+ genlmsg_cancel(skb, hdr);
+ ret = -EMSGSIZE;
+ goto out_put;
+ }
+
+ genlmsg_end(skb, hdr);
+ ret = skb->len;
+out_put:
+ for (i = 0; i < cnt; i++)
+ cache_put(items[i], cd);
+out_alloc:
+ kfree(seqnos);
+ kfree(items);
+ return ret;
+}
+
+/**
+ * sunrpc_nl_parse_one_ip_map - parse one ip_map entry from netlink
+ * @cd: cache_detail for the ip_map cache
+ * @attr: nested attribute containing ip_map fields
+ *
+ * Parses one ip_map entry from a netlink message and updates the
+ * cache. Mirrors the logic in ip_map_parse().
+ *
+ * Returns 0 on success or a negative errno.
+ */
+static int sunrpc_nl_parse_one_ip_map(struct cache_detail *cd,
+ struct nlattr *attr)
+{
+ struct nlattr *tb[SUNRPC_A_IP_MAP_EXPIRY + 1];
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in s4;
+ struct sockaddr_in6 s6;
+ } address;
+ struct sockaddr_in6 sin6;
+ struct ip_map *ipmp;
+ struct auth_domain *dom = NULL;
+ struct unix_domain *udom = NULL;
+ struct timespec64 boot;
+ time64_t expiry;
+ char class[8];
+ int err;
+ int len;
+
+ err = nla_parse_nested(tb, SUNRPC_A_IP_MAP_EXPIRY, attr,
+ sunrpc_ip_map_nl_policy, NULL);
+ if (err)
+ return err;
+
+ /* class (required) */
+ if (!tb[SUNRPC_A_IP_MAP_CLASS])
+ return -EINVAL;
+ len = nla_len(tb[SUNRPC_A_IP_MAP_CLASS]);
+ if (len <= 0 || len > sizeof(class))
+ return -EINVAL;
+ nla_strscpy(class, tb[SUNRPC_A_IP_MAP_CLASS], sizeof(class));
+
+ /* addr (required) */
+ if (!tb[SUNRPC_A_IP_MAP_ADDR])
+ return -EINVAL;
+ if (rpc_pton(cd->net, nla_data(tb[SUNRPC_A_IP_MAP_ADDR]),
+ nla_len(tb[SUNRPC_A_IP_MAP_ADDR]) - 1,
+ &address.sa, sizeof(address)) == 0)
+ return -EINVAL;
+
+ switch (address.sa.sa_family) {
+ case AF_INET:
+ sin6.sin6_family = AF_INET6;
+ ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
+ &sin6.sin6_addr);
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ memcpy(&sin6, &address.s6, sizeof(sin6));
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ /* expiry (required, wallclock seconds) */
+ if (!tb[SUNRPC_A_IP_MAP_EXPIRY])
+ return -EINVAL;
+ getboottime64(&boot);
+ expiry = nla_get_u64(tb[SUNRPC_A_IP_MAP_EXPIRY]) - boot.tv_sec;
+
+ /* domain name or negative */
+ if (tb[SUNRPC_A_IP_MAP_NEGATIVE]) {
+ udom = NULL;
+ } else if (tb[SUNRPC_A_IP_MAP_DOMAIN]) {
+ dom = unix_domain_find(nla_data(tb[SUNRPC_A_IP_MAP_DOMAIN]));
+ if (!dom)
+ return -ENOENT;
+ udom = container_of(dom, struct unix_domain, h);
+ } else {
+ return -EINVAL;
+ }
+
+ ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
+ if (ipmp)
+ err = __ip_map_update(cd, ipmp, udom, expiry);
+ else
+ err = -ENOMEM;
+
+ if (dom)
+ auth_domain_put(dom);
+
+ cache_flush();
+ return err;
+}
+
+/**
+ * sunrpc_nl_ip_map_set_reqs_doit - respond to ip_map requests
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Parse one or more ip_map cache responses from userspace and
+ * update the ip_map cache accordingly.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int sunrpc_nl_ip_map_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ const struct nlattr *attr;
+ int rem, ret = 0;
+
+ sn = net_generic(genl_info_net(info), sunrpc_net_id);
+
+ cd = sn->ip_map_cache;
+ if (!cd)
+ return -ENODEV;
+
+ nlmsg_for_each_attr_type(attr, SUNRPC_A_IP_MAP_REQS_REQUESTS,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ ret = sunrpc_nl_parse_one_ip_map(cd,
+ (struct nlattr *)attr);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
static const struct cache_detail ip_map_cache_template = {
.owner = THIS_MODULE,
.hash_size = IP_HASHMAX,
.name = "auth.unix.ip",
.cache_put = ip_map_put,
.cache_upcall = ip_map_upcall,
+ .cache_notify = ip_map_notify,
.cache_request = ip_map_request,
.cache_parse = ip_map_parse,
.cache_show = ip_map_show,
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 7be3de1a1aed..50e5e7f5b762 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -471,6 +471,7 @@ static void svc_tcp_handshake_done(void *data, int status, key_serial_t peerid)
}
clear_bit(XPT_HANDSHAKE, &xprt->xpt_flags);
complete_all(&svsk->sk_handshake_done);
+ svc_xprt_put(xprt);
}
/**
@@ -494,9 +495,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt)
clear_bit(XPT_TLS_SESSION, &xprt->xpt_flags);
init_completion(&svsk->sk_handshake_done);
+ /* Pin the transport across the asynchronous handshake callback. */
+ svc_xprt_get(xprt);
+
ret = tls_server_hello_x509(&args, GFP_KERNEL);
if (ret) {
trace_svc_tls_not_started(xprt);
+ svc_xprt_put(xprt);
goto out_failed;
}
@@ -505,8 +510,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt)
if (ret <= 0) {
if (tls_handshake_cancel(sk)) {
trace_svc_tls_timed_out(xprt);
+ svc_xprt_put(xprt);
goto out_close;
}
+ /* Cancellation lost to handshake_complete(): the
+ * callback is in flight and should finish quickly.
+ */
+ wait_for_completion(&svsk->sk_handshake_done);
}
if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags)) {
@@ -732,7 +742,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
.msg_flags = MSG_SPLICE_PAGES,
.msg_controllen = sizeof(buffer),
};
- unsigned int count;
+ int count;
int err;
svc_udp_release_ctxt(xprt, rqstp->rq_xprt_ctxt);
@@ -746,6 +756,10 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
goto out_notconn;
count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr);
+ if (count < 0) {
+ err = count;
+ goto out_trace;
+ }
iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
count, rqstp->rq_res.len);
@@ -757,6 +771,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
err = sock_sendmsg(svsk->sk_sock, &msg);
}
+out_trace:
trace_svcsock_udp_send(xprt, err);
mutex_unlock(&xprt->xpt_mutex);
@@ -1237,7 +1252,7 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp,
struct msghdr msg = {
.msg_flags = MSG_SPLICE_PAGES,
};
- unsigned int count;
+ int count;
void *buf;
int ret;
@@ -1253,10 +1268,15 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp,
count = xdr_buf_to_bvec(svsk->sk_bvec + 1, rqstp->rq_maxpages,
&rqstp->rq_res);
+ if (count < 0) {
+ ret = count;
+ goto out;
+ }
iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
1 + count, sizeof(marker) + rqstp->rq_res.len);
ret = sock_sendmsg(svsk->sk_sock, &msg);
+out:
page_frag_free(buf);
return ret;
}
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index e83d5d0be78b..fa6a30b5f046 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -139,19 +139,22 @@ xdr_free_bvec(struct xdr_buf *buf)
/**
* xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array
* @bvec: bio_vec array to populate
- * @bvec_size: element count of @bio_vec
+ * @bvec_size: element count of @bvec
* @xdr: xdr_buf to be copied
*
- * Returns the number of entries consumed in @bvec.
+ * Returns the number of entries consumed in @bvec on success, or
+ * -ESERVERFAULT when @xdr does not fit within @bvec_size entries.
*/
-unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
- const struct xdr_buf *xdr)
+int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+ const struct xdr_buf *xdr)
{
const struct kvec *head = xdr->head;
const struct kvec *tail = xdr->tail;
unsigned int count = 0;
if (head->iov_len) {
+ if (unlikely(count >= bvec_size))
+ goto bvec_overflow;
bvec_set_virt(bvec++, head->iov_base, head->iov_len);
++count;
}
@@ -165,29 +168,230 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
while (remaining > 0) {
len = min_t(unsigned int, remaining,
PAGE_SIZE - offset);
+ if (unlikely(count >= bvec_size))
+ goto bvec_overflow;
bvec_set_page(bvec++, *pages++, len, offset);
remaining -= len;
offset = 0;
- if (unlikely(++count > bvec_size))
- goto bvec_overflow;
+ ++count;
}
}
if (tail->iov_len) {
- bvec_set_virt(bvec, tail->iov_base, tail->iov_len);
- if (unlikely(++count > bvec_size))
+ if (unlikely(count >= bvec_size))
goto bvec_overflow;
+ bvec_set_virt(bvec, tail->iov_base, tail->iov_len);
+ ++count;
}
return count;
bvec_overflow:
pr_warn_once("%s: bio_vec array overflow\n", __func__);
- return count - 1;
+ return -ESERVERFAULT;
}
EXPORT_SYMBOL_GPL(xdr_buf_to_bvec);
/**
+ * xdr_buf_to_sg - Populate a scatterlist from an xdr_buf range
+ * @buf: xdr_buf to map
+ * @offset: starting byte offset within @buf
+ * @len: number of bytes to cover
+ * @sg: scatterlist array initialized with sg_init_table()
+ * @nsg: number of entries available in @sg
+ *
+ * @sg is traversed with sg_next(), so callers may pass a list
+ * assembled with sg_chain().
+ *
+ * Return: on success, the number of scatterlist entries used; the
+ * last used entry is marked with sg_mark_end(). On failure, a
+ * negative errno.
+ */
+int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg, unsigned int nsg)
+{
+ unsigned int page_len, thislen, page_offset;
+ struct scatterlist *cur = sg, *prev = NULL;
+ int nents = 0;
+ int i;
+
+ if (len == 0)
+ return 0;
+
+ if (offset >= buf->head[0].iov_len) {
+ offset -= buf->head[0].iov_len;
+ } else {
+ thislen = min_t(unsigned int,
+ buf->head[0].iov_len - offset, len);
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_buf(cur, buf->head[0].iov_base + offset,
+ thislen);
+ prev = cur;
+ cur = sg_next(cur);
+ nents++;
+ len -= thislen;
+ offset = 0;
+ }
+ if (len == 0)
+ goto done;
+
+ if (offset >= buf->page_len) {
+ offset -= buf->page_len;
+ } else {
+ page_len = min(buf->page_len - offset, len);
+ len -= page_len;
+ page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
+ i = (offset + buf->page_base) >> PAGE_SHIFT;
+ thislen = PAGE_SIZE - page_offset;
+ do {
+ if (thislen > page_len)
+ thislen = page_len;
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_page(cur, buf->pages[i],
+ thislen, page_offset);
+ prev = cur;
+ cur = sg_next(cur);
+ nents++;
+ page_len -= thislen;
+ i++;
+ page_offset = 0;
+ thislen = PAGE_SIZE;
+ } while (page_len != 0);
+ offset = 0;
+ }
+ if (len == 0)
+ goto done;
+
+ if (offset < buf->tail[0].iov_len) {
+ thislen = min_t(unsigned int,
+ buf->tail[0].iov_len - offset, len);
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_buf(cur, buf->tail[0].iov_base + offset,
+ thislen);
+ prev = cur;
+ nents++;
+ len -= thislen;
+ }
+ if (len != 0)
+ return -EINVAL;
+
+done:
+ if (prev)
+ sg_mark_end(prev);
+ return nents;
+}
+EXPORT_SYMBOL_GPL(xdr_buf_to_sg);
+
+/*
+ * Count the scatterlist entries needed to cover [offset, offset + len)
+ * within @buf. Mirrors the walk in xdr_buf_to_sg() so the caller can
+ * size an allocation that matches the requested sub-range rather than
+ * the full xdr_buf.
+ */
+static unsigned int xdr_buf_sg_nents(const struct xdr_buf *buf,
+ unsigned int offset, unsigned int len)
+{
+ unsigned int nsg = 0, thislen, page_offset;
+
+ if (len == 0)
+ return 0;
+
+ if (offset < buf->head[0].iov_len) {
+ thislen = min_t(unsigned int,
+ buf->head[0].iov_len - offset, len);
+ nsg++;
+ len -= thislen;
+ offset = 0;
+ } else {
+ offset -= buf->head[0].iov_len;
+ }
+ if (len == 0)
+ return nsg;
+
+ if (offset < buf->page_len) {
+ thislen = min(buf->page_len - offset, len);
+ page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
+ nsg += DIV_ROUND_UP(page_offset + thislen, PAGE_SIZE);
+ len -= thislen;
+ offset = 0;
+ } else {
+ offset -= buf->page_len;
+ }
+ if (len == 0)
+ return nsg;
+
+ if (offset < buf->tail[0].iov_len)
+ nsg++;
+ return nsg;
+}
+
+/**
+ * xdr_buf_to_sg_alloc - Populate a scatterlist for an xdr_buf range
+ * @buf: xdr_buf to map
+ * @offset: starting byte offset within @buf
+ * @len: number of bytes to cover
+ * @sg_head: caller-provided scatterlist array (typically stack-allocated)
+ * @sg_head_nents: number of entries in @sg_head
+ * @sg_overflow: OUT: chained extension, or NULL when @sg_head sufficed
+ * @gfp: memory allocation flags for overflow
+ *
+ * Populates @sg_head directly when the xdr_buf fits. When more
+ * entries are needed, an overflow scatterlist is allocated and
+ * chained from @sg_head so that the result is traversable with
+ * sg_next().
+ *
+ * Return: on success, the number of populated scatterlist entries
+ * (counting only data entries, not chain entries). @sg_head is
+ * the head of the resulting list. Caller must kfree @sg_overflow
+ * when done. On failure, a negative errno.
+ */
+int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg_head,
+ unsigned int sg_head_nents,
+ struct scatterlist **sg_overflow, gfp_t gfp)
+{
+ unsigned int nsg;
+ int ret;
+
+ *sg_overflow = NULL;
+ if (len == 0)
+ return 0;
+
+ nsg = xdr_buf_sg_nents(buf, offset, len);
+ if (nsg == 0)
+ return -EINVAL;
+
+ if (nsg <= sg_head_nents) {
+ sg_init_table(sg_head, nsg);
+ } else {
+ /* +1 replaces the slot sg_chain() consumes as the link. */
+ unsigned int overflow_nents = nsg - sg_head_nents + 1;
+ struct scatterlist *overflow;
+
+ overflow = kmalloc_array(overflow_nents, sizeof(*overflow),
+ gfp);
+ if (!overflow)
+ return -ENOMEM;
+
+ sg_init_table(sg_head, sg_head_nents);
+ sg_init_table(overflow, overflow_nents);
+ sg_chain(sg_head, sg_head_nents, overflow);
+ *sg_overflow = overflow;
+ }
+
+ ret = xdr_buf_to_sg(buf, offset, len, sg_head, nsg);
+ if (ret < 0) {
+ kfree(*sg_overflow);
+ *sg_overflow = NULL;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xdr_buf_to_sg_alloc);
+
+/**
* xdr_inline_pages - Prepare receive buffer for a large reply
* @xdr: xdr_buf into which reply will be placed
* @offset: expected offset where data payload will start, in bytes
@@ -2149,73 +2353,6 @@ int xdr_encode_array2(const struct xdr_buf *buf, unsigned int base,
}
EXPORT_SYMBOL_GPL(xdr_encode_array2);
-int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset,
- unsigned int len,
- int (*actor)(struct scatterlist *, void *), void *data)
-{
- int i, ret = 0;
- unsigned int page_len, thislen, page_offset;
- struct scatterlist sg[1];
-
- sg_init_table(sg, 1);
-
- if (offset >= buf->head[0].iov_len) {
- offset -= buf->head[0].iov_len;
- } else {
- thislen = buf->head[0].iov_len - offset;
- if (thislen > len)
- thislen = len;
- sg_set_buf(sg, buf->head[0].iov_base + offset, thislen);
- ret = actor(sg, data);
- if (ret)
- goto out;
- offset = 0;
- len -= thislen;
- }
- if (len == 0)
- goto out;
-
- if (offset >= buf->page_len) {
- offset -= buf->page_len;
- } else {
- page_len = buf->page_len - offset;
- if (page_len > len)
- page_len = len;
- len -= page_len;
- page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
- i = (offset + buf->page_base) >> PAGE_SHIFT;
- thislen = PAGE_SIZE - page_offset;
- do {
- if (thislen > page_len)
- thislen = page_len;
- sg_set_page(sg, buf->pages[i], thislen, page_offset);
- ret = actor(sg, data);
- if (ret)
- goto out;
- page_len -= thislen;
- i++;
- page_offset = 0;
- thislen = PAGE_SIZE;
- } while (page_len != 0);
- offset = 0;
- }
- if (len == 0)
- goto out;
- if (offset < buf->tail[0].iov_len) {
- thislen = buf->tail[0].iov_len - offset;
- if (thislen > len)
- thislen = len;
- sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen);
- ret = actor(sg, data);
- len -= thislen;
- }
- if (len != 0)
- ret = -EINVAL;
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(xdr_process_buf);
-
/**
* xdr_stream_decode_string_dup - Decode and duplicate variable length string
* @xdr: pointer to xdr_stream
diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c
index 415c0310101f..f67f0612b1a9 100644
--- a/net/sunrpc/xprtrdma/svc_rdma.c
+++ b/net/sunrpc/xprtrdma/svc_rdma.c
@@ -264,38 +264,22 @@ err:
return rc;
}
-struct workqueue_struct *svcrdma_wq;
-
void svc_rdma_cleanup(void)
{
svc_unreg_xprt_class(&svc_rdma_class);
svc_rdma_proc_cleanup();
- if (svcrdma_wq) {
- struct workqueue_struct *wq = svcrdma_wq;
-
- svcrdma_wq = NULL;
- destroy_workqueue(wq);
- }
dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n");
}
int svc_rdma_init(void)
{
- struct workqueue_struct *wq;
int rc;
- wq = alloc_workqueue("svcrdma", WQ_UNBOUND, 0);
- if (!wq)
- return -ENOMEM;
-
rc = svc_rdma_proc_init();
- if (rc) {
- destroy_workqueue(wq);
+ if (rc)
return rc;
- }
- svcrdma_wq = wq;
svc_reg_xprt_class(&svc_rdma_class);
dprintk("SVCRDMA Module Init, register RPC RDMA transport\n");
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index f8a0638eb095..fe9bf0371b6e 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -242,6 +242,10 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma,
* Ensure that the recv_ctxt is released whether or not a Reply
* was sent. For example, the client could close the connection,
* or svc_process could drop an RPC, before the Reply is sent.
+ *
+ * Also drain any send_ctxts queued for deferred release so that
+ * DMA unmap and page release run in nfsd thread context between
+ * RPCs rather than on the Send completion path.
*/
void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *vctxt)
{
@@ -251,6 +255,8 @@ void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *vctxt)
if (ctxt)
svc_rdma_recv_ctxt_put(rdma, ctxt);
+
+ svc_rdma_send_ctxts_drain(rdma);
}
static bool svc_rdma_refresh_recvs(struct svcxprt_rdma *rdma,
@@ -377,13 +383,16 @@ flushed:
trace_svcrdma_wc_recv_err(wc, &ctxt->rc_cid);
dropped:
svc_rdma_recv_ctxt_put(rdma, ctxt);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
* svc_rdma_flush_recv_queues - Drain pending Receive work
* @rdma: svcxprt_rdma being shut down
*
+ * Caller must guarantee that @rdma's Send and Recv Completion
+ * Queues are empty (e.g., via ib_drain_qp()), so that no completion
+ * handlers can still produce work on the queues being drained.
*/
void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma)
{
@@ -1001,7 +1010,7 @@ out_readlist:
if (ret == -EINVAL)
svc_rdma_send_error(rdma_xprt, ctxt, ret);
svc_rdma_recv_ctxt_put(rdma_xprt, ctxt);
- svc_xprt_deferred_close(xprt);
+ svc_rdma_xprt_deferred_close(rdma_xprt);
return ret;
}
return 0;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c
index 402e2ceca4ff..f7fd22cc4a59 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c
@@ -236,21 +236,12 @@ svc_rdma_write_info_alloc(struct svcxprt_rdma *rdma,
return info;
}
-static void svc_rdma_write_info_free_async(struct work_struct *work)
+static void svc_rdma_write_info_free(struct svc_rdma_write_info *info)
{
- struct svc_rdma_write_info *info;
-
- info = container_of(work, struct svc_rdma_write_info, wi_work);
svc_rdma_cc_release(info->wi_rdma, &info->wi_cc, DMA_TO_DEVICE);
kfree(info);
}
-static void svc_rdma_write_info_free(struct svc_rdma_write_info *info)
-{
- INIT_WORK(&info->wi_work, svc_rdma_write_info_free_async);
- queue_work(svcrdma_wq, &info->wi_work);
-}
-
/**
* svc_rdma_write_chunk_release - Release Write chunk I/O resources
* @rdma: controlling transport
@@ -313,7 +304,7 @@ static void svc_rdma_reply_done(struct ib_cq *cq, struct ib_wc *wc)
trace_svcrdma_wc_reply_err(wc, &cc->cc_cid);
}
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
@@ -345,7 +336,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc)
* some of the outgoing RPC message. Signal the loss
* to the client by closing the connection.
*/
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
@@ -390,7 +381,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc)
*/
svc_rdma_cc_release(rdma, cc, DMA_FROM_DEVICE);
svc_rdma_recv_ctxt_put(rdma, ctxt);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/*
@@ -754,216 +745,6 @@ int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma,
return xdr->len;
}
-/*
- * Cap contiguous RDMA Read sink allocations at order-4.
- * Higher orders risk allocation failure under
- * __GFP_NORETRY, which would negate the benefit of the
- * contiguous fast path.
- */
-#define SVC_RDMA_CONTIG_MAX_ORDER 4
-
-/**
- * svc_rdma_alloc_read_pages - Allocate physically contiguous pages
- * @nr_pages: number of pages needed
- * @order: on success, set to the allocation order
- *
- * Attempts a higher-order allocation, falling back to smaller orders.
- * The returned pages are split immediately so each sub-page has its
- * own refcount and can be freed independently.
- *
- * Returns a pointer to the first page on success, or NULL if even
- * order-1 allocation fails.
- */
-static struct page *
-svc_rdma_alloc_read_pages(unsigned int nr_pages, unsigned int *order)
-{
- unsigned int o;
- struct page *page;
-
- o = min(get_order(nr_pages << PAGE_SHIFT),
- SVC_RDMA_CONTIG_MAX_ORDER);
-
- while (o >= 1) {
- page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN,
- o);
- if (page) {
- split_page(page, o);
- *order = o;
- return page;
- }
- o--;
- }
- return NULL;
-}
-
-/*
- * svc_rdma_fill_contig_bvec - Replace rq_pages with a contiguous allocation
- * @rqstp: RPC transaction context
- * @head: context for ongoing I/O
- * @bv: bvec entry to fill
- * @pages_left: number of data pages remaining in the segment
- * @len_left: bytes remaining in the segment
- *
- * On success, fills @bv with a bvec spanning the contiguous range and
- * advances rc_curpage/rc_page_count. Returns the byte length covered,
- * or zero if the allocation failed or would overrun rq_maxpages.
- */
-static unsigned int
-svc_rdma_fill_contig_bvec(struct svc_rqst *rqstp,
- struct svc_rdma_recv_ctxt *head,
- struct bio_vec *bv, unsigned int pages_left,
- unsigned int len_left)
-{
- unsigned int order, npages, chunk_pages, chunk_len, i;
- struct page *page;
-
- page = svc_rdma_alloc_read_pages(pages_left, &order);
- if (!page)
- return 0;
- npages = 1 << order;
-
- if (head->rc_curpage + npages > rqstp->rq_maxpages) {
- for (i = 0; i < npages; i++)
- __free_page(page + i);
- return 0;
- }
-
- /*
- * Replace rq_pages[] entries with pages from the contiguous
- * allocation. If npages exceeds chunk_pages, the extra pages
- * stay in rq_pages[] for later reuse or normal rqst teardown.
- */
- for (i = 0; i < npages; i++) {
- svc_rqst_page_release(rqstp,
- rqstp->rq_pages[head->rc_curpage + i]);
- rqstp->rq_pages[head->rc_curpage + i] = page + i;
- }
-
- chunk_pages = min(npages, pages_left);
- chunk_len = min_t(unsigned int, chunk_pages << PAGE_SHIFT, len_left);
- bvec_set_page(bv, page, chunk_len, 0);
- head->rc_page_count += chunk_pages;
- head->rc_curpage += chunk_pages;
- return chunk_len;
-}
-
-/*
- * svc_rdma_fill_page_bvec - Add a single rq_page to the bvec array
- * @head: context for ongoing I/O
- * @ctxt: R/W context whose bvec array is being filled
- * @cur: page to add
- * @bvec_idx: pointer to current bvec index, not advanced on merge
- * @len_left: bytes remaining in the segment
- *
- * If @cur is physically contiguous with the preceding bvec, it is
- * merged by extending that bvec's length. Otherwise a new bvec
- * entry is created. Returns the byte length covered.
- */
-static unsigned int
-svc_rdma_fill_page_bvec(struct svc_rdma_recv_ctxt *head,
- struct svc_rdma_rw_ctxt *ctxt, struct page *cur,
- unsigned int *bvec_idx, unsigned int len_left)
-{
- unsigned int chunk_len = min_t(unsigned int, PAGE_SIZE, len_left);
-
- head->rc_page_count++;
- head->rc_curpage++;
-
- if (*bvec_idx > 0) {
- struct bio_vec *prev = &ctxt->rw_bvec[*bvec_idx - 1];
-
- if (page_to_phys(prev->bv_page) + prev->bv_offset +
- prev->bv_len == page_to_phys(cur)) {
- prev->bv_len += chunk_len;
- return chunk_len;
- }
- }
-
- bvec_set_page(&ctxt->rw_bvec[*bvec_idx], cur, chunk_len, 0);
- (*bvec_idx)++;
- return chunk_len;
-}
-
-/**
- * svc_rdma_build_read_segment_contig - Build RDMA Read WR with contiguous pages
- * @rqstp: RPC transaction context
- * @head: context for ongoing I/O
- * @segment: co-ordinates of remote memory to be read
- *
- * Greedily allocates higher-order pages to cover the segment,
- * building one bvec per contiguous chunk. Each allocation is
- * split so sub-pages have independent refcounts. When a
- * higher-order allocation fails, remaining pages are covered
- * individually, merging adjacent pages into the preceding bvec
- * when they are physically contiguous. The split sub-pages
- * replace entries in rq_pages[] so downstream cleanup is
- * unchanged.
- *
- * Returns:
- * %0: the Read WR was constructed successfully
- * %-ENOMEM: allocation failed
- * %-EIO: a DMA mapping error occurred
- */
-static int svc_rdma_build_read_segment_contig(struct svc_rqst *rqstp,
- struct svc_rdma_recv_ctxt *head,
- const struct svc_rdma_segment *segment)
-{
- struct svcxprt_rdma *rdma = svc_rdma_rqst_rdma(rqstp);
- struct svc_rdma_chunk_ctxt *cc = &head->rc_cc;
- unsigned int nr_data_pages, bvec_idx;
- struct svc_rdma_rw_ctxt *ctxt;
- unsigned int len_left;
- int ret;
-
- nr_data_pages = PAGE_ALIGN(segment->rs_length) >> PAGE_SHIFT;
- if (head->rc_curpage + nr_data_pages > rqstp->rq_maxpages)
- return -ENOMEM;
-
- ctxt = svc_rdma_get_rw_ctxt(rdma, nr_data_pages);
- if (!ctxt)
- return -ENOMEM;
-
- bvec_idx = 0;
- len_left = segment->rs_length;
- while (len_left) {
- unsigned int pages_left = PAGE_ALIGN(len_left) >> PAGE_SHIFT;
- unsigned int chunk_len = 0;
-
- if (pages_left >= 2)
- chunk_len = svc_rdma_fill_contig_bvec(rqstp, head,
- &ctxt->rw_bvec[bvec_idx],
- pages_left, len_left);
- if (chunk_len) {
- bvec_idx++;
- } else {
- struct page *cur =
- rqstp->rq_pages[head->rc_curpage];
- chunk_len = svc_rdma_fill_page_bvec(head, ctxt, cur,
- &bvec_idx,
- len_left);
- }
-
- len_left -= chunk_len;
- }
-
- ctxt->rw_nents = bvec_idx;
-
- head->rc_pageoff = offset_in_page(segment->rs_length);
- if (head->rc_pageoff)
- head->rc_curpage--;
-
- ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset,
- segment->rs_handle, segment->rs_length,
- DMA_FROM_DEVICE);
- if (ret < 0)
- return -EIO;
- percpu_counter_inc(&svcrdma_stat_read);
-
- list_add(&ctxt->rw_list, &cc->cc_rwctxts);
- cc->cc_sqecount += ret;
- return 0;
-}
-
/**
* svc_rdma_build_read_segment - Build RDMA Read WQEs to pull one RDMA segment
* @rqstp: RPC transaction context
@@ -990,14 +771,6 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
if (check_add_overflow(head->rc_pageoff, len, &total))
return -EINVAL;
nr_bvec = PAGE_ALIGN(total) >> PAGE_SHIFT;
-
- if (head->rc_pageoff == 0 && nr_bvec >= 2) {
- ret = svc_rdma_build_read_segment_contig(rqstp, head,
- segment);
- if (ret != -ENOMEM)
- return ret;
- }
-
ctxt = svc_rdma_get_rw_ctxt(rdma, nr_bvec);
if (!ctxt)
return -ENOMEM;
@@ -1343,11 +1116,6 @@ static void svc_rdma_clear_rqst_pages(struct svc_rqst *rqstp,
{
unsigned int i;
- /*
- * Move only pages containing RPC data into rc_pages[]. Pages
- * from a contiguous allocation that were not used for the
- * payload remain in rq_pages[] for subsequent reuse.
- */
for (i = 0; i < head->rc_page_count; i++) {
head->rc_pages[i] = rqstp->rq_pages[i];
rqstp->rq_pages[i] = NULL;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index 8b3f0c8c14b2..7f6d17bf8c1f 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -79,21 +79,21 @@
* The ownership of all of the Reply's pages are transferred into that
* ctxt, the Send WR is posted, and sendto returns.
*
- * The svc_rdma_send_ctxt is presented when the Send WR completes. The
- * Send completion handler finally releases the Reply's pages.
- *
- * This mechanism also assumes that completions on the transport's Send
- * Completion Queue do not run in parallel. Otherwise a Write completion
- * and Send completion running at the same time could release pages that
- * are still DMA-mapped.
+ * The svc_rdma_send_ctxt is presented when the Send WR completes.
+ * The Send completion handler queues the send_ctxt onto the
+ * per-transport sc_send_release_list (a lock-free llist). The
+ * nfsd thread drains sc_send_release_list in xpo_release_ctxt
+ * between RPCs, DMA-unmapping SGEs, releasing chunk I/O
+ * resources and pages, and returning send_ctxts to the free
+ * list in a batch.
*
* Error Handling
*
* - If the Send WR is posted successfully, it will either complete
* successfully, or get flushed. Either way, the Send completion
- * handler releases the Reply's pages.
- * - If the Send WR cannot be not posted, the forward path releases
- * the Reply's pages.
+ * handler queues the send_ctxt for deferred release.
+ * - If the Send WR cannot be posted, the forward path releases the
+ * Reply's pages.
*
* This handles the case, without the use of page reference counting,
* where two different Write segments send portions of the same page.
@@ -226,14 +226,25 @@ out:
return ctxt;
out_empty:
+ svc_rdma_send_ctxts_drain(rdma);
+
+ spin_lock(&rdma->sc_send_lock);
+ node = llist_del_first(&rdma->sc_send_ctxts);
+ spin_unlock(&rdma->sc_send_lock);
+ if (node) {
+ ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node);
+ goto out;
+ }
+
ctxt = svc_rdma_send_ctxt_alloc(rdma);
if (!ctxt)
return NULL;
goto out;
}
-static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
- struct svc_rdma_send_ctxt *ctxt)
+/* Release chunk I/O resources and DMA-unmap SGEs. */
+static void svc_rdma_send_ctxt_unmap(struct svcxprt_rdma *rdma,
+ struct svc_rdma_send_ctxt *ctxt)
{
struct ib_device *device = rdma->sc_cm_id->device;
unsigned int i;
@@ -241,9 +252,6 @@ static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
svc_rdma_write_chunk_release(rdma, ctxt);
svc_rdma_reply_chunk_release(rdma, ctxt);
- if (ctxt->sc_page_count)
- release_pages(ctxt->sc_pages, ctxt->sc_page_count);
-
/* The first SGE contains the transport header, which
* remains mapped until @ctxt is destroyed.
*/
@@ -256,30 +264,56 @@ static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
ctxt->sc_sges[i].length,
DMA_TO_DEVICE);
}
+}
+
+/* Unmap, release pages, and return send_ctxt to the free list. */
+static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
+ struct svc_rdma_send_ctxt *ctxt)
+{
+ svc_rdma_send_ctxt_unmap(rdma, ctxt);
+
+ if (ctxt->sc_page_count)
+ release_pages(ctxt->sc_pages, ctxt->sc_page_count);
llist_add(&ctxt->sc_node, &rdma->sc_send_ctxts);
}
-static void svc_rdma_send_ctxt_put_async(struct work_struct *work)
+/**
+ * svc_rdma_send_ctxts_drain - Release completed send_ctxts
+ * @rdma: controlling svcxprt_rdma
+ */
+void svc_rdma_send_ctxts_drain(struct svcxprt_rdma *rdma)
{
- struct svc_rdma_send_ctxt *ctxt;
+ struct svc_rdma_send_ctxt *ctxt, *next;
+ struct llist_node *node;
- ctxt = container_of(work, struct svc_rdma_send_ctxt, sc_work);
- svc_rdma_send_ctxt_release(ctxt->sc_rdma, ctxt);
+ node = llist_del_all(&rdma->sc_send_release_list);
+ llist_for_each_entry_safe(ctxt, next, node, sc_node)
+ svc_rdma_send_ctxt_release(rdma, ctxt);
}
/**
- * svc_rdma_send_ctxt_put - Return send_ctxt to free list
+ * svc_rdma_send_ctxt_put - Queue send_ctxt for deferred release
* @rdma: controlling svcxprt_rdma
- * @ctxt: object to return to the free list
+ * @ctxt: send_ctxt to queue for deferred release
+ *
+ * Queues @ctxt onto sc_send_release_list. DMA unmap and
+ * page release run later in svc_rdma_send_ctxts_drain(),
+ * typically from xpo_release_ctxt.
*
- * Pages left in sc_pages are DMA unmapped and released.
+ * On the empty-to-non-empty transition, set XPT_DATA and
+ * enqueue the transport. Without this self-trigger, a Send
+ * completion arriving after the last xpo_release_ctxt on an
+ * idle connection would leave the send_ctxt's DMA mappings
+ * and reply pages pinned until another drain occurred.
*/
void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *ctxt)
{
- INIT_WORK(&ctxt->sc_work, svc_rdma_send_ctxt_put_async);
- queue_work(svcrdma_wq, &ctxt->sc_work);
+ if (llist_add(&ctxt->sc_node, &rdma->sc_send_release_list)) {
+ set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags);
+ svc_xprt_enqueue(&rdma->sc_xprt);
+ }
}
/**
@@ -367,6 +401,15 @@ int svc_rdma_sq_wait(struct svcxprt_rdma *rdma,
atomic_inc(&rdma->sc_sq_ticket_tail);
wake_up(&rdma->sc_sq_ticket_wait);
trace_svcrdma_sq_retry(rdma, cid);
+
+ /*
+ * While this thread sat on sc_send_wait or sc_sq_ticket_wait,
+ * Send completions that tried to enqueue this transport for a
+ * release-list drain were rejected: svc_rdma_has_wspace returns
+ * 0 while either waitqueue is active, and svc_xprt_ready
+ * rejects the enqueue. Drain the release list now.
+ */
+ svc_rdma_send_ctxts_drain(rdma);
return 0;
out_close:
@@ -395,7 +438,7 @@ int svc_rdma_post_send_err(struct svcxprt_rdma *rdma,
int sqecount, int ret)
{
trace_svcrdma_sq_post_err(rdma, cid, ret);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
/* If even one WR was posted, a Send completion will
* return the reserved SQ slots.
@@ -437,7 +480,7 @@ flushed:
else
trace_svcrdma_wc_send_flush(wc, &ctxt->sc_cid);
svc_rdma_send_ctxt_put(rdma, ctxt);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
@@ -1158,7 +1201,7 @@ put_ctxt:
svc_rdma_send_ctxt_put(rdma, sctxt);
drop_connection:
trace_svcrdma_send_err(rqstp, ret);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
return -ENOTCONN;
}
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index f18bc60d9f4f..7ca71741106b 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -98,10 +98,27 @@ struct svc_xprt_class svc_rdma_class = {
.xcl_ident = XPRT_TRANSPORT_RDMA,
};
+/**
+ * svc_rdma_xprt_deferred_close - Close an RDMA transport (deferred)
+ * @rdma: transport to close
+ */
+void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma)
+{
+ svc_xprt_deferred_close(&rdma->sc_xprt);
+
+ /* Release parked sc_sq_ticket_wait and sc_send_wait waiters.
+ * Once XPT_CLOSE is observed each returns -ENOTCONN.
+ */
+ wake_up_all(&rdma->sc_sq_ticket_wait);
+ wake_up_all(&rdma->sc_send_wait);
+}
+
/* QP event handler */
static void qp_event_handler(struct ib_event *event, void *context)
{
struct svc_xprt *xprt = context;
+ struct svcxprt_rdma *rdma =
+ container_of(xprt, struct svcxprt_rdma, sc_xprt);
trace_svcrdma_qp_error(event, (struct sockaddr *)&xprt->xpt_remote);
switch (event->event) {
@@ -119,7 +136,7 @@ static void qp_event_handler(struct ib_event *event, void *context)
case IB_EVENT_QP_ACCESS_ERR:
case IB_EVENT_DEVICE_FATAL:
default:
- svc_xprt_deferred_close(xprt);
+ svc_rdma_xprt_deferred_close(rdma);
break;
}
}
@@ -178,6 +195,7 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv,
init_llist_head(&cma_xprt->sc_send_ctxts);
init_llist_head(&cma_xprt->sc_recv_ctxts);
init_llist_head(&cma_xprt->sc_rw_ctxts);
+ init_llist_head(&cma_xprt->sc_send_release_list);
init_waitqueue_head(&cma_xprt->sc_send_wait);
init_waitqueue_head(&cma_xprt->sc_sq_ticket_wait);
@@ -340,7 +358,7 @@ static int svc_rdma_cma_handler(struct rdma_cm_id *cma_id,
svc_xprt_enqueue(xprt);
break;
case RDMA_CM_EVENT_DISCONNECTED:
- svc_xprt_deferred_close(xprt);
+ svc_rdma_xprt_deferred_close(rdma);
break;
default:
break;
@@ -597,6 +615,15 @@ static void svc_rdma_detach(struct svc_xprt *xprt)
container_of(xprt, struct svcxprt_rdma, sc_xprt);
rdma_disconnect(rdma->sc_cm_id);
+
+ /*
+ * Most close paths go through svc_rdma_xprt_deferred_close(),
+ * which wakes the SQ waitqueues. svc_xprt_close() reaches
+ * detach without that helper, so wake any threads parked in
+ * svc_rdma_sq_wait() here as well.
+ */
+ wake_up_all(&rdma->sc_sq_ticket_wait);
+ wake_up_all(&rdma->sc_send_wait);
}
/**
@@ -614,7 +641,7 @@ static void svc_rdma_free(struct svc_xprt *xprt)
/* This blocks until the Completion Queues are empty */
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
ib_drain_qp(rdma->sc_qp);
- flush_workqueue(svcrdma_wq);
+ svc_rdma_send_ctxts_drain(rdma);
svc_rdma_flush_recv_queues(rdma);