mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 11:31:31 +00:00
NFS Client Updates for Linux 6.12
New Features: * Add a 'noalignwrite' mount option for lock-less 'lost writes' prevention * Add support for the LOCALIO protocol extention Bugfixes: * Fix memory leak in error path of nfs4_do_reclaim() * Simplify and guarantee lock owner uniqueness * Fix -Wformat-truncation warning * Fix folio refcounts by using folio_attach_private() * Fix failing the mount system call when the server is down * Fix detection of "Proxying of Times" server support Cleanups: * Annotate struct nfs_cache_array with __counted_by() * Remove unnecessary NULL checks before kfree() * Convert RPC_TASK_* constants to an enum * Remove obsolete or misleading comments and declerations -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmbzKDAACgkQ18tUv7Cl QOusjBAAxTSoVbHocl+9eYpvKnscPArgPXnfd6mB9rnQRgtnceTO2ei7cdiE2qhz dxQiyzlAXh3e7dGwoy02qEd6wTTqWeQ8ESdMpCAqSacBU4tu5owfzNSWunZvgYYj QOhjdmv8M1IfZnTstPlVrRNaZcDkhV1tzEtpZppkEqhTB0bHWqrcM4EdklTWT0Yc PGMpGbfuGsa4qZy2vWl7doERVEgK8mBeahLtYFD2W6phIvNWgD6IlKy66RaK2RfH nXmZoZbI2/ioi4TKvNyY8xoGMGvetLI1h8YNQYkEg060XCkisLZDOvoodUAylOTR 2jHQLG5+/ejhpD/zgPghGZDSGNN1GyZaH09E/vtiS+3k9OXxFz6Rq68VnC6kpMA4 TIUYsT8ejPzs2gW59iDFGB6cKI4XnRtxgmApW/Za0y9A72PSi+G/pbWAk7ThjTxf +HySsba4baA63opIgBSLVBrUsXZfdn/KTDTZ4nkPiq57BggGcZv7Y2ItOTXA+pB/ 5nigDKkhWsYVjMbkx6wmh+VO2gv4/Z8WqsmiDwFMpVqM0w8eycBOHjOumuuc6nmw y+2OKZqU2Npm2HI/R8lA7nB1m2QP5t7CRM2+xlZNuavHrfsMaqHNl8/9VgxlCATQ /Zo74hbhmCgQYxrTjL8XFQG9/8y0o3H5IcTEr/SgCVxHyDSan1I= =YjyC -----END PGP SIGNATURE----- Merge tag 'nfs-for-6.12-1' of git://git.linux-nfs.org/projects/anna/linux-nfs Pull NFS client updates from Anna Schumaker: "New Features: - Add a 'noalignwrite' mount option for lock-less 'lost writes' prevention - Add support for the LOCALIO protocol extention Bugfixes: - Fix memory leak in error path of nfs4_do_reclaim() - Simplify and guarantee lock owner uniqueness - Fix -Wformat-truncation warning - Fix folio refcounts by using folio_attach_private() - Fix failing the mount system call when the server is down - Fix detection of "Proxying of Times" server support Cleanups: - Annotate struct nfs_cache_array with __counted_by() - Remove unnecessary NULL checks before kfree() - Convert RPC_TASK_* constants to an enum - Remove obsolete or misleading comments and declerations" * tag 'nfs-for-6.12-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (41 commits) nfs: Fix `make htmldocs` warnings in the localio documentation nfs: add "NFS Client and Server Interlock" section to localio.rst nfs: add FAQ section to Documentation/filesystems/nfs/localio.rst nfs: add Documentation/filesystems/nfs/localio.rst nfs: implement client support for NFS_LOCALIO_PROGRAM nfs/localio: use dedicated workqueues for filesystem read and write pnfs/flexfiles: enable localio support nfs: enable localio for non-pNFS IO nfs: add LOCALIO support nfs: pass struct nfsd_file to nfs_init_pgio and nfs_init_commit nfsd: implement server support for NFS_LOCALIO_PROGRAM nfsd: add LOCALIO support nfs_common: prepare for the NFS client to use nfsd_file for LOCALIO nfs_common: add NFS LOCALIO auxiliary protocol enablement SUNRPC: replace program list with program array SUNRPC: add svcauth_map_clnt_to_svc_cred_local SUNRPC: remove call_allocate() BUG_ONs nfsd: add nfsd_serv_try_get and nfsd_serv_put nfsd: add nfsd_file_acquire_local() nfsd: factor out __fh_verify to allow NULL rqstp to be passed ...
This commit is contained in:
commit
684a64bf32
@ -8,6 +8,7 @@ NFS
|
||||
|
||||
client-identifier
|
||||
exporting
|
||||
localio
|
||||
pnfs
|
||||
rpc-cache
|
||||
rpc-server-gss
|
||||
|
357
Documentation/filesystems/nfs/localio.rst
Normal file
357
Documentation/filesystems/nfs/localio.rst
Normal file
@ -0,0 +1,357 @@
|
||||
===========
|
||||
NFS LOCALIO
|
||||
===========
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The LOCALIO auxiliary RPC protocol allows the Linux NFS client and
|
||||
server to reliably handshake to determine if they are on the same
|
||||
host. Select "NFS client and server support for LOCALIO auxiliary
|
||||
protocol" in menuconfig to enable CONFIG_NFS_LOCALIO in the kernel
|
||||
config (both CONFIG_NFS_FS and CONFIG_NFSD must also be enabled).
|
||||
|
||||
Once an NFS client and server handshake as "local", the client will
|
||||
bypass the network RPC protocol for read, write and commit operations.
|
||||
Due to this XDR and RPC bypass, these operations will operate faster.
|
||||
|
||||
The LOCALIO auxiliary protocol's implementation, which uses the same
|
||||
connection as NFS traffic, follows the pattern established by the NFS
|
||||
ACL protocol extension.
|
||||
|
||||
The LOCALIO auxiliary protocol is needed to allow robust discovery of
|
||||
clients local to their servers. In a private implementation that
|
||||
preceded use of this LOCALIO protocol, a fragile sockaddr network
|
||||
address based match against all local network interfaces was attempted.
|
||||
But unlike the LOCALIO protocol, the sockaddr-based matching didn't
|
||||
handle use of iptables or containers.
|
||||
|
||||
The robust handshake between local client and server is just the
|
||||
beginning, the ultimate use case this locality makes possible is the
|
||||
client is able to open files and issue reads, writes and commits
|
||||
directly to the server without having to go over the network. The
|
||||
requirement is to perform these loopback NFS operations as efficiently
|
||||
as possible, this is particularly useful for container use cases
|
||||
(e.g. kubernetes) where it is possible to run an IO job local to the
|
||||
server.
|
||||
|
||||
The performance advantage realized from LOCALIO's ability to bypass
|
||||
using XDR and RPC for reads, writes and commits can be extreme, e.g.:
|
||||
|
||||
fio for 20 secs with directio, qd of 8, 16 libaio threads:
|
||||
- With LOCALIO:
|
||||
4K read: IOPS=979k, BW=3825MiB/s (4011MB/s)(74.7GiB/20002msec)
|
||||
4K write: IOPS=165k, BW=646MiB/s (678MB/s)(12.6GiB/20002msec)
|
||||
128K read: IOPS=402k, BW=49.1GiB/s (52.7GB/s)(982GiB/20002msec)
|
||||
128K write: IOPS=11.5k, BW=1433MiB/s (1503MB/s)(28.0GiB/20004msec)
|
||||
|
||||
- Without LOCALIO:
|
||||
4K read: IOPS=79.2k, BW=309MiB/s (324MB/s)(6188MiB/20003msec)
|
||||
4K write: IOPS=59.8k, BW=234MiB/s (245MB/s)(4671MiB/20002msec)
|
||||
128K read: IOPS=33.9k, BW=4234MiB/s (4440MB/s)(82.7GiB/20004msec)
|
||||
128K write: IOPS=11.5k, BW=1434MiB/s (1504MB/s)(28.0GiB/20011msec)
|
||||
|
||||
fio for 20 secs with directio, qd of 8, 1 libaio thread:
|
||||
- With LOCALIO:
|
||||
4K read: IOPS=230k, BW=898MiB/s (941MB/s)(17.5GiB/20001msec)
|
||||
4K write: IOPS=22.6k, BW=88.3MiB/s (92.6MB/s)(1766MiB/20001msec)
|
||||
128K read: IOPS=38.8k, BW=4855MiB/s (5091MB/s)(94.8GiB/20001msec)
|
||||
128K write: IOPS=11.4k, BW=1428MiB/s (1497MB/s)(27.9GiB/20001msec)
|
||||
|
||||
- Without LOCALIO:
|
||||
4K read: IOPS=77.1k, BW=301MiB/s (316MB/s)(6022MiB/20001msec)
|
||||
4K write: IOPS=32.8k, BW=128MiB/s (135MB/s)(2566MiB/20001msec)
|
||||
128K read: IOPS=24.4k, BW=3050MiB/s (3198MB/s)(59.6GiB/20001msec)
|
||||
128K write: IOPS=11.4k, BW=1430MiB/s (1500MB/s)(27.9GiB/20001msec)
|
||||
|
||||
FAQ
|
||||
===
|
||||
|
||||
1. What are the use cases for LOCALIO?
|
||||
|
||||
a. Workloads where the NFS client and server are on the same host
|
||||
realize improved IO performance. In particular, it is common when
|
||||
running containerised workloads for jobs to find themselves
|
||||
running on the same host as the knfsd server being used for
|
||||
storage.
|
||||
|
||||
2. What are the requirements for LOCALIO?
|
||||
|
||||
a. Bypass use of the network RPC protocol as much as possible. This
|
||||
includes bypassing XDR and RPC for open, read, write and commit
|
||||
operations.
|
||||
b. Allow client and server to autonomously discover if they are
|
||||
running local to each other without making any assumptions about
|
||||
the local network topology.
|
||||
c. Support the use of containers by being compatible with relevant
|
||||
namespaces (e.g. network, user, mount).
|
||||
d. Support all versions of NFS. NFSv3 is of particular importance
|
||||
because it has wide enterprise usage and pNFS flexfiles makes use
|
||||
of it for the data path.
|
||||
|
||||
3. Why doesn’t LOCALIO just compare IP addresses or hostnames when
|
||||
deciding if the NFS client and server are co-located on the same
|
||||
host?
|
||||
|
||||
Since one of the main use cases is containerised workloads, we cannot
|
||||
assume that IP addresses will be shared between the client and
|
||||
server. This sets up a requirement for a handshake protocol that
|
||||
needs to go over the same connection as the NFS traffic in order to
|
||||
identify that the client and the server really are running on the
|
||||
same host. The handshake uses a secret that is sent over the wire,
|
||||
and can be verified by both parties by comparing with a value stored
|
||||
in shared kernel memory if they are truly co-located.
|
||||
|
||||
4. Does LOCALIO improve pNFS flexfiles?
|
||||
|
||||
Yes, LOCALIO complements pNFS flexfiles by allowing it to take
|
||||
advantage of NFS client and server locality. Policy that initiates
|
||||
client IO as closely to the server where the data is stored naturally
|
||||
benefits from the data path optimization LOCALIO provides.
|
||||
|
||||
5. Why not develop a new pNFS layout to enable LOCALIO?
|
||||
|
||||
A new pNFS layout could be developed, but doing so would put the
|
||||
onus on the server to somehow discover that the client is co-located
|
||||
when deciding to hand out the layout.
|
||||
There is value in a simpler approach (as provided by LOCALIO) that
|
||||
allows the NFS client to negotiate and leverage locality without
|
||||
requiring more elaborate modeling and discovery of such locality in a
|
||||
more centralized manner.
|
||||
|
||||
6. Why is having the client perform a server-side file OPEN, without
|
||||
using RPC, beneficial? Is the benefit pNFS specific?
|
||||
|
||||
Avoiding the use of XDR and RPC for file opens is beneficial to
|
||||
performance regardless of whether pNFS is used. Especially when
|
||||
dealing with small files its best to avoid going over the wire
|
||||
whenever possible, otherwise it could reduce or even negate the
|
||||
benefits of avoiding the wire for doing the small file I/O itself.
|
||||
Given LOCALIO's requirements the current approach of having the
|
||||
client perform a server-side file open, without using RPC, is ideal.
|
||||
If in the future requirements change then we can adapt accordingly.
|
||||
|
||||
7. Why is LOCALIO only supported with UNIX Authentication (AUTH_UNIX)?
|
||||
|
||||
Strong authentication is usually tied to the connection itself. It
|
||||
works by establishing a context that is cached by the server, and
|
||||
that acts as the key for discovering the authorisation token, which
|
||||
can then be passed to rpc.mountd to complete the authentication
|
||||
process. On the other hand, in the case of AUTH_UNIX, the credential
|
||||
that was passed over the wire is used directly as the key in the
|
||||
upcall to rpc.mountd. This simplifies the authentication process, and
|
||||
so makes AUTH_UNIX easier to support.
|
||||
|
||||
8. How do export options that translate RPC user IDs behave for LOCALIO
|
||||
operations (eg. root_squash, all_squash)?
|
||||
|
||||
Export options that translate user IDs are managed by nfsd_setuser()
|
||||
which is called by nfsd_setuser_and_check_port() which is called by
|
||||
__fh_verify(). So they get handled exactly the same way for LOCALIO
|
||||
as they do for non-LOCALIO.
|
||||
|
||||
9. How does LOCALIO make certain that object lifetimes are managed
|
||||
properly given NFSD and NFS operate in different contexts?
|
||||
|
||||
See the detailed "NFS Client and Server Interlock" section below.
|
||||
|
||||
RPC
|
||||
===
|
||||
|
||||
The LOCALIO auxiliary RPC protocol consists of a single "UUID_IS_LOCAL"
|
||||
RPC method that allows the Linux NFS client to verify the local Linux
|
||||
NFS server can see the nonce (single-use UUID) the client generated and
|
||||
made available in nfs_common. This protocol isn't part of an IETF
|
||||
standard, nor does it need to be considering it is Linux-to-Linux
|
||||
auxiliary RPC protocol that amounts to an implementation detail.
|
||||
|
||||
The UUID_IS_LOCAL method encodes the client generated uuid_t in terms of
|
||||
the fixed UUID_SIZE (16 bytes). The fixed size opaque encode and decode
|
||||
XDR methods are used instead of the less efficient variable sized
|
||||
methods.
|
||||
|
||||
The RPC program number for the NFS_LOCALIO_PROGRAM is 400122 (as assigned
|
||||
by IANA, see https://www.iana.org/assignments/rpc-program-numbers/ ):
|
||||
Linux Kernel Organization 400122 nfslocalio
|
||||
|
||||
The LOCALIO protocol spec in rpcgen syntax is::
|
||||
|
||||
/* raw RFC 9562 UUID */
|
||||
#define UUID_SIZE 16
|
||||
typedef u8 uuid_t<UUID_SIZE>;
|
||||
|
||||
program NFS_LOCALIO_PROGRAM {
|
||||
version LOCALIO_V1 {
|
||||
void
|
||||
NULL(void) = 0;
|
||||
|
||||
void
|
||||
UUID_IS_LOCAL(uuid_t) = 1;
|
||||
} = 1;
|
||||
} = 400122;
|
||||
|
||||
LOCALIO uses the same transport connection as NFS traffic. As such,
|
||||
LOCALIO is not registered with rpcbind.
|
||||
|
||||
NFS Common and Client/Server Handshake
|
||||
======================================
|
||||
|
||||
fs/nfs_common/nfslocalio.c provides interfaces that enable an NFS client
|
||||
to generate a nonce (single-use UUID) and associated short-lived
|
||||
nfs_uuid_t struct, register it with nfs_common for subsequent lookup and
|
||||
verification by the NFS server and if matched the NFS server populates
|
||||
members in the nfs_uuid_t struct. The NFS client then uses nfs_common to
|
||||
transfer the nfs_uuid_t from its nfs_uuids to the nn->nfsd_serv
|
||||
clients_list from the nfs_common's uuids_list. See:
|
||||
fs/nfs/localio.c:nfs_local_probe()
|
||||
|
||||
nfs_common's nfs_uuids list is the basis for LOCALIO enablement, as such
|
||||
it has members that point to nfsd memory for direct use by the client
|
||||
(e.g. 'net' is the server's network namespace, through it the client can
|
||||
access nn->nfsd_serv with proper rcu read access). It is this client
|
||||
and server synchronization that enables advanced usage and lifetime of
|
||||
objects to span from the host kernel's nfsd to per-container knfsd
|
||||
instances that are connected to nfs client's running on the same local
|
||||
host.
|
||||
|
||||
NFS Client and Server Interlock
|
||||
===============================
|
||||
|
||||
LOCALIO provides the nfs_uuid_t object and associated interfaces to
|
||||
allow proper network namespace (net-ns) and NFSD object refcounting:
|
||||
|
||||
We don't want to keep a long-term counted reference on each NFSD's
|
||||
net-ns in the client because that prevents a server container from
|
||||
completely shutting down.
|
||||
|
||||
So we avoid taking a reference at all and rely on the per-cpu
|
||||
reference to the server (detailed below) being sufficient to keep
|
||||
the net-ns active. This involves allowing the NFSD's net-ns exit
|
||||
code to iterate all active clients and clear their ->net pointers
|
||||
(which are needed to find the per-cpu-refcount for the nfsd_serv).
|
||||
|
||||
Details:
|
||||
|
||||
- Embed nfs_uuid_t in nfs_client. nfs_uuid_t provides a list_head
|
||||
that can be used to find the client. It does add the 16-byte
|
||||
uuid_t to nfs_client so it is bigger than needed (given that
|
||||
uuid_t is only used during the initial NFS client and server
|
||||
LOCALIO handshake to determine if they are local to each other).
|
||||
If that is really a problem we can find a fix.
|
||||
|
||||
- When the nfs server confirms that the uuid_t is local, it moves
|
||||
the nfs_uuid_t onto a per-net-ns list in NFSD's nfsd_net.
|
||||
|
||||
- When each server's net-ns is shutting down - in a "pre_exit"
|
||||
handler, all these nfs_uuid_t have their ->net cleared. There is
|
||||
an rcu_synchronize() call between pre_exit() handlers and exit()
|
||||
handlers so any caller that sees nfs_uuid_t ->net as not NULL can
|
||||
safely manage the per-cpu-refcount for nfsd_serv.
|
||||
|
||||
- The client's nfs_uuid_t is passed to nfsd_open_local_fh() so it
|
||||
can safely dereference ->net in a private rcu_read_lock() section
|
||||
to allow safe access to the associated nfsd_net and nfsd_serv.
|
||||
|
||||
So LOCALIO required the introduction and use of NFSD's percpu_ref to
|
||||
interlock nfsd_destroy_serv() and nfsd_open_local_fh(), to ensure each
|
||||
nn->nfsd_serv is not destroyed while in use by nfsd_open_local_fh(), and
|
||||
warrants a more detailed explanation:
|
||||
|
||||
nfsd_open_local_fh() uses nfsd_serv_try_get() before opening its
|
||||
nfsd_file handle and then the caller (NFS client) must drop the
|
||||
reference for the nfsd_file and associated nn->nfsd_serv using
|
||||
nfs_file_put_local() once it has completed its IO.
|
||||
|
||||
This interlock working relies heavily on nfsd_open_local_fh() being
|
||||
afforded the ability to safely deal with the possibility that the
|
||||
NFSD's net-ns (and nfsd_net by association) may have been destroyed
|
||||
by nfsd_destroy_serv() via nfsd_shutdown_net() -- which is only
|
||||
possible given the nfs_uuid_t ->net pointer managemenet detailed
|
||||
above.
|
||||
|
||||
All told, this elaborate interlock of the NFS client and server has been
|
||||
verified to fix an easy to hit crash that would occur if an NFSD
|
||||
instance running in a container, with a LOCALIO client mounted, is
|
||||
shutdown. Upon restart of the container and associated NFSD the client
|
||||
would go on to crash due to NULL pointer dereference that occurred due
|
||||
to the LOCALIO client's attempting to nfsd_open_local_fh(), using
|
||||
nn->nfsd_serv, without having a proper reference on nn->nfsd_serv.
|
||||
|
||||
NFS Client issues IO instead of Server
|
||||
======================================
|
||||
|
||||
Because LOCALIO is focused on protocol bypass to achieve improved IO
|
||||
performance, alternatives to the traditional NFS wire protocol (SUNRPC
|
||||
with XDR) must be provided to access the backing filesystem.
|
||||
|
||||
See fs/nfs/localio.c:nfs_local_open_fh() and
|
||||
fs/nfsd/localio.c:nfsd_open_local_fh() for the interface that makes
|
||||
focused use of select nfs server objects to allow a client local to a
|
||||
server to open a file pointer without needing to go over the network.
|
||||
|
||||
The client's fs/nfs/localio.c:nfs_local_open_fh() will call into the
|
||||
server's fs/nfsd/localio.c:nfsd_open_local_fh() and carefully access
|
||||
both the associated nfsd network namespace and nn->nfsd_serv in terms of
|
||||
RCU. If nfsd_open_local_fh() finds that the client no longer sees valid
|
||||
nfsd objects (be it struct net or nn->nfsd_serv) it returns -ENXIO
|
||||
to nfs_local_open_fh() and the client will try to reestablish the
|
||||
LOCALIO resources needed by calling nfs_local_probe() again. This
|
||||
recovery is needed if/when an nfsd instance running in a container were
|
||||
to reboot while a LOCALIO client is connected to it.
|
||||
|
||||
Once the client has an open nfsd_file pointer it will issue reads,
|
||||
writes and commits directly to the underlying local filesystem (normally
|
||||
done by the nfs server). As such, for these operations, the NFS client
|
||||
is issuing IO to the underlying local filesystem that it is sharing with
|
||||
the NFS server. See: fs/nfs/localio.c:nfs_local_doio() and
|
||||
fs/nfs/localio.c:nfs_local_commit().
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
Localio is only supported when UNIX-style authentication (AUTH_UNIX, aka
|
||||
AUTH_SYS) is used.
|
||||
|
||||
Care is taken to ensure the same NFS security mechanisms are used
|
||||
(authentication, etc) regardless of whether LOCALIO or regular NFS
|
||||
access is used. The auth_domain established as part of the traditional
|
||||
NFS client access to the NFS server is also used for LOCALIO.
|
||||
|
||||
Relative to containers, LOCALIO gives the client access to the network
|
||||
namespace the server has. This is required to allow the client to access
|
||||
the server's per-namespace nfsd_net struct. With traditional NFS, the
|
||||
client is afforded this same level of access (albeit in terms of the NFS
|
||||
protocol via SUNRPC). No other namespaces (user, mount, etc) have been
|
||||
altered or purposely extended from the server to the client.
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
The LOCALIO auxiliary protocol and associated NFS LOCALIO read, write
|
||||
and commit access have proven stable against various test scenarios:
|
||||
|
||||
- Client and server both on the same host.
|
||||
|
||||
- All permutations of client and server support enablement for both
|
||||
local and remote client and server.
|
||||
|
||||
- Testing against NFS storage products that don't support the LOCALIO
|
||||
protocol was also performed.
|
||||
|
||||
- Client on host, server within a container (for both v3 and v4.2).
|
||||
The container testing was in terms of podman managed containers and
|
||||
includes successful container stop/restart scenario.
|
||||
|
||||
- Formalizing these test scenarios in terms of existing test
|
||||
infrastructure is on-going. Initial regular coverage is provided in
|
||||
terms of ktest running xfstests against a LOCALIO-enabled NFS loopback
|
||||
mount configuration, and includes lockdep and KASAN coverage, see:
|
||||
https://evilpiepirate.org/~testdashboard/ci?user=snitzer&branch=snitm-nfs-next
|
||||
https://github.com/koverstreet/ktest
|
||||
|
||||
- Various kdevops testing (in terms of "Chuck's BuildBot") has been
|
||||
performed to regularly verify the LOCALIO changes haven't caused any
|
||||
regressions to non-LOCALIO NFS use cases.
|
||||
|
||||
- All of Hammerspace's various sanity tests pass with LOCALIO enabled
|
||||
(this includes numerous pNFS and flexfiles tests).
|
23
fs/Kconfig
23
fs/Kconfig
@ -386,6 +386,29 @@ config NFS_COMMON
|
||||
depends on NFSD || NFS_FS || LOCKD
|
||||
default y
|
||||
|
||||
config NFS_COMMON_LOCALIO_SUPPORT
|
||||
tristate
|
||||
default n
|
||||
default y if NFSD=y || NFS_FS=y
|
||||
default m if NFSD=m && NFS_FS=m
|
||||
select SUNRPC
|
||||
|
||||
config NFS_LOCALIO
|
||||
bool "NFS client and server support for LOCALIO auxiliary protocol"
|
||||
depends on NFSD && NFS_FS
|
||||
select NFS_COMMON_LOCALIO_SUPPORT
|
||||
default n
|
||||
help
|
||||
Some NFS servers support an auxiliary NFS LOCALIO protocol
|
||||
that is not an official part of the NFS protocol.
|
||||
|
||||
This option enables support for the LOCALIO protocol in the
|
||||
kernel's NFS server and client. Enable this to permit local
|
||||
NFS clients to bypass the network when issuing reads and
|
||||
writes to the local NFS server.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config NFS_V4_2_SSC_HELPER
|
||||
bool
|
||||
default y if NFS_V4_2
|
||||
|
@ -4,6 +4,7 @@ config NFS_FS
|
||||
depends on INET && FILE_LOCKING && MULTIUSER
|
||||
select LOCKD
|
||||
select SUNRPC
|
||||
select NFS_COMMON
|
||||
select NFS_ACL_SUPPORT if NFS_V3_ACL
|
||||
help
|
||||
Choose Y here if you want to access files residing on other
|
||||
|
@ -13,6 +13,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
|
||||
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
|
||||
nfs-$(CONFIG_SYSCTL) += sysctl.o
|
||||
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o
|
||||
nfs-$(CONFIG_NFS_LOCALIO) += localio.o
|
||||
|
||||
obj-$(CONFIG_NFS_V2) += nfsv2.o
|
||||
nfsv2-y := nfs2super.o proc.o nfs2xdr.o
|
||||
|
@ -178,6 +178,14 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
|
||||
clp->cl_max_connect = cl_init->max_connect ? cl_init->max_connect : 1;
|
||||
clp->cl_net = get_net(cl_init->net);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
seqlock_init(&clp->cl_boot_lock);
|
||||
ktime_get_real_ts64(&clp->cl_nfssvc_boot);
|
||||
clp->cl_uuid.net = NULL;
|
||||
clp->cl_uuid.dom = NULL;
|
||||
spin_lock_init(&clp->cl_localio_lock);
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
|
||||
clp->cl_principal = "*";
|
||||
clp->cl_xprtsec = cl_init->xprtsec;
|
||||
return clp;
|
||||
@ -233,6 +241,8 @@ static void pnfs_init_server(struct nfs_server *server)
|
||||
*/
|
||||
void nfs_free_client(struct nfs_client *clp)
|
||||
{
|
||||
nfs_local_disable(clp);
|
||||
|
||||
/* -EIO all pending I/O */
|
||||
if (!IS_ERR(clp->cl_rpcclient))
|
||||
rpc_shutdown_client(clp->cl_rpcclient);
|
||||
@ -424,7 +434,10 @@ struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
|
||||
list_add_tail(&new->cl_share_link,
|
||||
&nn->nfs_client_list);
|
||||
spin_unlock(&nn->nfs_client_lock);
|
||||
return rpc_ops->init_client(new, cl_init);
|
||||
new = rpc_ops->init_client(new, cl_init);
|
||||
if (!IS_ERR(new))
|
||||
nfs_local_probe(new);
|
||||
return new;
|
||||
}
|
||||
|
||||
spin_unlock(&nn->nfs_client_lock);
|
||||
@ -997,8 +1010,8 @@ struct nfs_server *nfs_alloc_server(void)
|
||||
init_waitqueue_head(&server->write_congestion_wait);
|
||||
atomic_long_set(&server->writeback, 0);
|
||||
|
||||
ida_init(&server->openowner_id);
|
||||
ida_init(&server->lockowner_id);
|
||||
atomic64_set(&server->owner_ctr, 0);
|
||||
|
||||
pnfs_init_server(server);
|
||||
rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC");
|
||||
|
||||
@ -1037,8 +1050,6 @@ void nfs_free_server(struct nfs_server *server)
|
||||
}
|
||||
ida_free(&s_sysfs_ids, server->s_sysfs_id);
|
||||
|
||||
ida_destroy(&server->lockowner_id);
|
||||
ida_destroy(&server->openowner_id);
|
||||
put_cred(server->cred);
|
||||
nfs_release_automount_timer();
|
||||
call_rcu(&server->rcu, delayed_free);
|
||||
|
@ -151,7 +151,7 @@ struct nfs_cache_array {
|
||||
unsigned char folio_full : 1,
|
||||
folio_is_eof : 1,
|
||||
cookies_are_ordered : 1;
|
||||
struct nfs_cache_array_entry array[];
|
||||
struct nfs_cache_array_entry array[] __counted_by(size);
|
||||
};
|
||||
|
||||
struct nfs_readdir_descriptor {
|
||||
@ -328,7 +328,8 @@ static int nfs_readdir_folio_array_append(struct folio *folio,
|
||||
goto out;
|
||||
}
|
||||
|
||||
cache_entry = &array->array[array->size];
|
||||
array->size++;
|
||||
cache_entry = &array->array[array->size - 1];
|
||||
cache_entry->cookie = array->last_cookie;
|
||||
cache_entry->ino = entry->ino;
|
||||
cache_entry->d_type = entry->d_type;
|
||||
@ -337,7 +338,6 @@ static int nfs_readdir_folio_array_append(struct folio *folio,
|
||||
array->last_cookie = entry->cookie;
|
||||
if (array->last_cookie <= cache_entry->cookie)
|
||||
array->cookies_are_ordered = 0;
|
||||
array->size++;
|
||||
if (entry->eof != 0)
|
||||
nfs_readdir_array_set_eof(array);
|
||||
out:
|
||||
|
@ -488,7 +488,7 @@ filelayout_read_pagelist(struct nfs_pgio_header *hdr)
|
||||
/* Perform an asynchronous read to ds */
|
||||
nfs_initiate_pgio(ds_clnt, hdr, hdr->cred,
|
||||
NFS_PROTO(hdr->inode), &filelayout_read_call_ops,
|
||||
0, RPC_TASK_SOFTCONN);
|
||||
0, RPC_TASK_SOFTCONN, NULL);
|
||||
return PNFS_ATTEMPTED;
|
||||
}
|
||||
|
||||
@ -530,7 +530,7 @@ filelayout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
|
||||
/* Perform an asynchronous write */
|
||||
nfs_initiate_pgio(ds_clnt, hdr, hdr->cred,
|
||||
NFS_PROTO(hdr->inode), &filelayout_write_call_ops,
|
||||
sync, RPC_TASK_SOFTCONN);
|
||||
sync, RPC_TASK_SOFTCONN, NULL);
|
||||
return PNFS_ATTEMPTED;
|
||||
}
|
||||
|
||||
@ -1011,7 +1011,7 @@ static int filelayout_initiate_commit(struct nfs_commit_data *data, int how)
|
||||
data->args.fh = fh;
|
||||
return nfs_initiate_commit(ds_clnt, data, NFS_PROTO(data->inode),
|
||||
&filelayout_commit_call_ops, how,
|
||||
RPC_TASK_SOFTCONN);
|
||||
RPC_TASK_SOFTCONN, NULL);
|
||||
out_err:
|
||||
pnfs_generic_prepare_to_resend_writes(data);
|
||||
pnfs_generic_commit_release(data);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/nfs_mount.h>
|
||||
#include <linux/nfs_page.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/sched/mm.h>
|
||||
|
||||
#include <linux/sunrpc/metrics.h>
|
||||
@ -162,6 +163,21 @@ decode_name(struct xdr_stream *xdr, u32 *id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nfsd_file *
|
||||
ff_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
||||
struct nfs_fh *fh, fmode_t mode)
|
||||
{
|
||||
if (mode & FMODE_WRITE) {
|
||||
/*
|
||||
* Always request read and write access since this corresponds
|
||||
* to a rw layout.
|
||||
*/
|
||||
mode |= FMODE_READ;
|
||||
}
|
||||
|
||||
return nfs_local_open_fh(clp, cred, fh, mode);
|
||||
}
|
||||
|
||||
static bool ff_mirror_match_fh(const struct nfs4_ff_layout_mirror *m1,
|
||||
const struct nfs4_ff_layout_mirror *m2)
|
||||
{
|
||||
@ -237,7 +253,7 @@ static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(gfp_t gfp_flags)
|
||||
|
||||
static void ff_layout_free_mirror(struct nfs4_ff_layout_mirror *mirror)
|
||||
{
|
||||
const struct cred *cred;
|
||||
const struct cred *cred;
|
||||
|
||||
ff_layout_remove_mirror(mirror);
|
||||
kfree(mirror->fh_versions);
|
||||
@ -1756,6 +1772,7 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
|
||||
struct pnfs_layout_segment *lseg = hdr->lseg;
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
struct rpc_clnt *ds_clnt;
|
||||
struct nfsd_file *localio;
|
||||
struct nfs4_ff_layout_mirror *mirror;
|
||||
const struct cred *ds_cred;
|
||||
loff_t offset = hdr->args.offset;
|
||||
@ -1802,11 +1819,18 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
|
||||
hdr->args.offset = offset;
|
||||
hdr->mds_offset = offset;
|
||||
|
||||
/* Start IO accounting for local read */
|
||||
localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh, FMODE_READ);
|
||||
if (localio) {
|
||||
hdr->task.tk_start = ktime_get();
|
||||
ff_layout_read_record_layoutstats_start(&hdr->task, hdr);
|
||||
}
|
||||
|
||||
/* Perform an asynchronous read to ds */
|
||||
nfs_initiate_pgio(ds_clnt, hdr, ds_cred, ds->ds_clp->rpc_ops,
|
||||
vers == 3 ? &ff_layout_read_call_ops_v3 :
|
||||
&ff_layout_read_call_ops_v4,
|
||||
0, RPC_TASK_SOFTCONN);
|
||||
0, RPC_TASK_SOFTCONN, localio);
|
||||
put_cred(ds_cred);
|
||||
return PNFS_ATTEMPTED;
|
||||
|
||||
@ -1826,6 +1850,7 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
|
||||
struct pnfs_layout_segment *lseg = hdr->lseg;
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
struct rpc_clnt *ds_clnt;
|
||||
struct nfsd_file *localio;
|
||||
struct nfs4_ff_layout_mirror *mirror;
|
||||
const struct cred *ds_cred;
|
||||
loff_t offset = hdr->args.offset;
|
||||
@ -1870,11 +1895,19 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
|
||||
*/
|
||||
hdr->args.offset = offset;
|
||||
|
||||
/* Start IO accounting for local write */
|
||||
localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
|
||||
FMODE_READ|FMODE_WRITE);
|
||||
if (localio) {
|
||||
hdr->task.tk_start = ktime_get();
|
||||
ff_layout_write_record_layoutstats_start(&hdr->task, hdr);
|
||||
}
|
||||
|
||||
/* Perform an asynchronous write */
|
||||
nfs_initiate_pgio(ds_clnt, hdr, ds_cred, ds->ds_clp->rpc_ops,
|
||||
vers == 3 ? &ff_layout_write_call_ops_v3 :
|
||||
&ff_layout_write_call_ops_v4,
|
||||
sync, RPC_TASK_SOFTCONN);
|
||||
sync, RPC_TASK_SOFTCONN, localio);
|
||||
put_cred(ds_cred);
|
||||
return PNFS_ATTEMPTED;
|
||||
|
||||
@ -1908,6 +1941,7 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
|
||||
struct pnfs_layout_segment *lseg = data->lseg;
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
struct rpc_clnt *ds_clnt;
|
||||
struct nfsd_file *localio;
|
||||
struct nfs4_ff_layout_mirror *mirror;
|
||||
const struct cred *ds_cred;
|
||||
u32 idx;
|
||||
@ -1946,10 +1980,18 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
|
||||
if (fh)
|
||||
data->args.fh = fh;
|
||||
|
||||
/* Start IO accounting for local commit */
|
||||
localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
|
||||
FMODE_READ|FMODE_WRITE);
|
||||
if (localio) {
|
||||
data->task.tk_start = ktime_get();
|
||||
ff_layout_commit_record_layoutstats_start(&data->task, data);
|
||||
}
|
||||
|
||||
ret = nfs_initiate_commit(ds_clnt, data, ds->ds_clp->rpc_ops,
|
||||
vers == 3 ? &ff_layout_commit_call_ops_v3 :
|
||||
&ff_layout_commit_call_ops_v4,
|
||||
how, RPC_TASK_SOFTCONN);
|
||||
how, RPC_TASK_SOFTCONN, localio);
|
||||
put_cred(ds_cred);
|
||||
return ret;
|
||||
out_err:
|
||||
@ -2086,12 +2128,6 @@ static int ff_layout_encode_ioerr(struct xdr_stream *xdr,
|
||||
return ff_layout_encode_ds_ioerr(xdr, &ff_args->errors);
|
||||
}
|
||||
|
||||
static void
|
||||
encode_opaque_fixed(struct xdr_stream *xdr, const void *buf, size_t len)
|
||||
{
|
||||
WARN_ON_ONCE(xdr_stream_encode_opaque_fixed(xdr, buf, len) < 0);
|
||||
}
|
||||
|
||||
static void
|
||||
ff_layout_encode_ff_iostat_head(struct xdr_stream *xdr,
|
||||
const nfs4_stateid *stateid,
|
||||
|
@ -395,6 +395,12 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
|
||||
|
||||
/* connect success, check rsize/wsize limit */
|
||||
if (!status) {
|
||||
/*
|
||||
* ds_clp is put in destroy_ds().
|
||||
* keep ds_clp even if DS is local, so that if local IO cannot
|
||||
* proceed somehow, we can fall back to NFS whenever we want.
|
||||
*/
|
||||
nfs_local_probe(ds->ds_clp);
|
||||
max_payload =
|
||||
nfs_block_size(rpc_max_payload(ds->ds_clp->cl_rpcclient),
|
||||
NULL);
|
||||
|
@ -49,6 +49,7 @@ enum nfs_param {
|
||||
Opt_bsize,
|
||||
Opt_clientaddr,
|
||||
Opt_cto,
|
||||
Opt_alignwrite,
|
||||
Opt_fg,
|
||||
Opt_fscache,
|
||||
Opt_fscache_flag,
|
||||
@ -149,6 +150,7 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = {
|
||||
fsparam_u32 ("bsize", Opt_bsize),
|
||||
fsparam_string("clientaddr", Opt_clientaddr),
|
||||
fsparam_flag_no("cto", Opt_cto),
|
||||
fsparam_flag_no("alignwrite", Opt_alignwrite),
|
||||
fsparam_flag ("fg", Opt_fg),
|
||||
fsparam_flag_no("fsc", Opt_fscache_flag),
|
||||
fsparam_string("fsc", Opt_fscache),
|
||||
@ -592,6 +594,12 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
|
||||
else
|
||||
ctx->flags |= NFS_MOUNT_TRUNK_DISCOVERY;
|
||||
break;
|
||||
case Opt_alignwrite:
|
||||
if (result.negated)
|
||||
ctx->flags |= NFS_MOUNT_NO_ALIGNWRITE;
|
||||
else
|
||||
ctx->flags &= ~NFS_MOUNT_NO_ALIGNWRITE;
|
||||
break;
|
||||
case Opt_ac:
|
||||
if (result.negated)
|
||||
ctx->flags |= NFS_MOUNT_NOAC;
|
||||
|
@ -62,7 +62,7 @@ static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *i
|
||||
}
|
||||
|
||||
/*
|
||||
* get an NFS2/NFS3 root dentry from the root filehandle
|
||||
* get a root dentry from the root filehandle
|
||||
*/
|
||||
int nfs_get_root(struct super_block *s, struct fs_context *fc)
|
||||
{
|
||||
|
@ -2461,35 +2461,54 @@ static void nfs_destroy_inodecache(void)
|
||||
kmem_cache_destroy(nfs_inode_cachep);
|
||||
}
|
||||
|
||||
struct workqueue_struct *nfslocaliod_workqueue;
|
||||
struct workqueue_struct *nfsiod_workqueue;
|
||||
EXPORT_SYMBOL_GPL(nfsiod_workqueue);
|
||||
|
||||
/*
|
||||
* start up the nfsiod workqueue
|
||||
*/
|
||||
static int nfsiod_start(void)
|
||||
{
|
||||
struct workqueue_struct *wq;
|
||||
dprintk("RPC: creating workqueue nfsiod\n");
|
||||
wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
|
||||
if (wq == NULL)
|
||||
return -ENOMEM;
|
||||
nfsiod_workqueue = wq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the nfsiod workqueue
|
||||
* Destroy the nfsiod workqueues
|
||||
*/
|
||||
static void nfsiod_stop(void)
|
||||
{
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
wq = nfsiod_workqueue;
|
||||
if (wq == NULL)
|
||||
return;
|
||||
nfsiod_workqueue = NULL;
|
||||
destroy_workqueue(wq);
|
||||
if (wq != NULL) {
|
||||
nfsiod_workqueue = NULL;
|
||||
destroy_workqueue(wq);
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
wq = nfslocaliod_workqueue;
|
||||
if (wq != NULL) {
|
||||
nfslocaliod_workqueue = NULL;
|
||||
destroy_workqueue(wq);
|
||||
}
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the nfsiod workqueues
|
||||
*/
|
||||
static int nfsiod_start(void)
|
||||
{
|
||||
dprintk("RPC: creating workqueue nfsiod\n");
|
||||
nfsiod_workqueue = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
|
||||
if (nfsiod_workqueue == NULL)
|
||||
return -ENOMEM;
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
/*
|
||||
* localio writes need to use a normal (non-memreclaim) workqueue.
|
||||
* When we start getting low on space, XFS goes and calls flush_work() on
|
||||
* a non-memreclaim work queue, which causes a priority inversion problem.
|
||||
*/
|
||||
dprintk("RPC: creating workqueue nfslocaliod\n");
|
||||
nfslocaliod_workqueue = alloc_workqueue("nfslocaliod", WQ_UNBOUND, 0);
|
||||
if (unlikely(nfslocaliod_workqueue == NULL)) {
|
||||
nfsiod_stop();
|
||||
return -ENOMEM;
|
||||
}
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int nfs_net_id;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/sunrpc/addr.h>
|
||||
#include <linux/nfs_page.h>
|
||||
#include <linux/nfslocalio.h>
|
||||
#include <linux/wait_bit.h>
|
||||
|
||||
#define NFS_SB_MASK (SB_RDONLY|SB_NOSUID|SB_NODEV|SB_NOEXEC|SB_SYNCHRONOUS)
|
||||
@ -308,7 +309,8 @@ void nfs_pgio_header_free(struct nfs_pgio_header *);
|
||||
int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
|
||||
int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
|
||||
const struct cred *cred, const struct nfs_rpc_ops *rpc_ops,
|
||||
const struct rpc_call_ops *call_ops, int how, int flags);
|
||||
const struct rpc_call_ops *call_ops, int how, int flags,
|
||||
struct nfsd_file *localio);
|
||||
void nfs_free_request(struct nfs_page *req);
|
||||
struct nfs_pgio_mirror *
|
||||
nfs_pgio_current_mirror(struct nfs_pageio_descriptor *desc);
|
||||
@ -438,6 +440,7 @@ int nfs_check_flags(int);
|
||||
|
||||
/* inode.c */
|
||||
extern struct workqueue_struct *nfsiod_workqueue;
|
||||
extern struct workqueue_struct *nfslocaliod_workqueue;
|
||||
extern struct inode *nfs_alloc_inode(struct super_block *sb);
|
||||
extern void nfs_free_inode(struct inode *);
|
||||
extern int nfs_write_inode(struct inode *, struct writeback_control *);
|
||||
@ -449,6 +452,51 @@ extern void nfs_set_cache_invalid(struct inode *inode, unsigned long flags);
|
||||
extern bool nfs_check_cache_invalid(struct inode *, unsigned long);
|
||||
extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
/* localio.c */
|
||||
extern void nfs_local_disable(struct nfs_client *);
|
||||
extern void nfs_local_probe(struct nfs_client *);
|
||||
extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
|
||||
const struct cred *,
|
||||
struct nfs_fh *,
|
||||
const fmode_t);
|
||||
extern int nfs_local_doio(struct nfs_client *,
|
||||
struct nfsd_file *,
|
||||
struct nfs_pgio_header *,
|
||||
const struct rpc_call_ops *);
|
||||
extern int nfs_local_commit(struct nfsd_file *,
|
||||
struct nfs_commit_data *,
|
||||
const struct rpc_call_ops *, int);
|
||||
extern bool nfs_server_is_local(const struct nfs_client *clp);
|
||||
|
||||
#else /* CONFIG_NFS_LOCALIO */
|
||||
static inline void nfs_local_disable(struct nfs_client *clp) {}
|
||||
static inline void nfs_local_probe(struct nfs_client *clp) {}
|
||||
static inline struct nfsd_file *
|
||||
nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
||||
struct nfs_fh *fh, const fmode_t mode)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline int nfs_local_doio(struct nfs_client *clp,
|
||||
struct nfsd_file *localio,
|
||||
struct nfs_pgio_header *hdr,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int nfs_local_commit(struct nfsd_file *localio,
|
||||
struct nfs_commit_data *data,
|
||||
const struct rpc_call_ops *call_ops, int how)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline bool nfs_server_is_local(const struct nfs_client *clp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
|
||||
/* super.c */
|
||||
extern const struct super_operations nfs_sops;
|
||||
bool nfs_auth_info_match(const struct nfs_auth_info *, rpc_authflavor_t);
|
||||
@ -505,7 +553,6 @@ extern int nfs_read_add_folio(struct nfs_pageio_descriptor *pgio,
|
||||
struct nfs_open_context *ctx,
|
||||
struct folio *folio);
|
||||
extern void nfs_pageio_complete_read(struct nfs_pageio_descriptor *pgio);
|
||||
extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
|
||||
extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio);
|
||||
|
||||
/* super.c */
|
||||
@ -528,7 +575,8 @@ extern int nfs_initiate_commit(struct rpc_clnt *clnt,
|
||||
struct nfs_commit_data *data,
|
||||
const struct nfs_rpc_ops *nfs_ops,
|
||||
const struct rpc_call_ops *call_ops,
|
||||
int how, int flags);
|
||||
int how, int flags,
|
||||
struct nfsd_file *localio);
|
||||
extern void nfs_init_commit(struct nfs_commit_data *data,
|
||||
struct list_head *head,
|
||||
struct pnfs_layout_segment *lseg,
|
||||
|
757
fs/nfs/localio.c
Normal file
757
fs/nfs/localio.c
Normal file
@ -0,0 +1,757 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NFS client support for local clients to bypass network stack
|
||||
*
|
||||
* Copyright (C) 2014 Weston Andros Adamson <dros@primarydata.com>
|
||||
* Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com>
|
||||
* Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
|
||||
* Copyright (C) 2024 NeilBrown <neilb@suse.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/sunrpc/addr.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <linux/nfs_common.h>
|
||||
#include <linux/nfslocalio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bvec.h>
|
||||
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_xdr.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "pnfs.h"
|
||||
#include "nfstrace.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_VFS
|
||||
|
||||
struct nfs_local_kiocb {
|
||||
struct kiocb kiocb;
|
||||
struct bio_vec *bvec;
|
||||
struct nfs_pgio_header *hdr;
|
||||
struct work_struct work;
|
||||
struct nfsd_file *localio;
|
||||
};
|
||||
|
||||
struct nfs_local_fsync_ctx {
|
||||
struct nfsd_file *localio;
|
||||
struct nfs_commit_data *data;
|
||||
struct work_struct work;
|
||||
struct kref kref;
|
||||
struct completion *done;
|
||||
};
|
||||
static void nfs_local_fsync_work(struct work_struct *work);
|
||||
|
||||
static bool localio_enabled __read_mostly = true;
|
||||
module_param(localio_enabled, bool, 0644);
|
||||
|
||||
static inline bool nfs_client_is_local(const struct nfs_client *clp)
|
||||
{
|
||||
return !!test_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
|
||||
}
|
||||
|
||||
bool nfs_server_is_local(const struct nfs_client *clp)
|
||||
{
|
||||
return nfs_client_is_local(clp) && localio_enabled;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_server_is_local);
|
||||
|
||||
/*
|
||||
* UUID_IS_LOCAL XDR functions
|
||||
*/
|
||||
|
||||
static void localio_xdr_enc_uuidargs(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
const void *data)
|
||||
{
|
||||
const u8 *uuid = data;
|
||||
|
||||
encode_opaque_fixed(xdr, uuid, UUID_SIZE);
|
||||
}
|
||||
|
||||
static int localio_xdr_dec_uuidres(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
void *result)
|
||||
{
|
||||
/* void return */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rpc_procinfo nfs_localio_procedures[] = {
|
||||
[LOCALIOPROC_UUID_IS_LOCAL] = {
|
||||
.p_proc = LOCALIOPROC_UUID_IS_LOCAL,
|
||||
.p_encode = localio_xdr_enc_uuidargs,
|
||||
.p_decode = localio_xdr_dec_uuidres,
|
||||
.p_arglen = XDR_QUADLEN(UUID_SIZE),
|
||||
.p_replen = 0,
|
||||
.p_statidx = LOCALIOPROC_UUID_IS_LOCAL,
|
||||
.p_name = "UUID_IS_LOCAL",
|
||||
},
|
||||
};
|
||||
|
||||
static unsigned int nfs_localio_counts[ARRAY_SIZE(nfs_localio_procedures)];
|
||||
static const struct rpc_version nfslocalio_version1 = {
|
||||
.number = 1,
|
||||
.nrprocs = ARRAY_SIZE(nfs_localio_procedures),
|
||||
.procs = nfs_localio_procedures,
|
||||
.counts = nfs_localio_counts,
|
||||
};
|
||||
|
||||
static const struct rpc_version *nfslocalio_version[] = {
|
||||
[1] = &nfslocalio_version1,
|
||||
};
|
||||
|
||||
extern const struct rpc_program nfslocalio_program;
|
||||
static struct rpc_stat nfslocalio_rpcstat = { &nfslocalio_program };
|
||||
|
||||
const struct rpc_program nfslocalio_program = {
|
||||
.name = "nfslocalio",
|
||||
.number = NFS_LOCALIO_PROGRAM,
|
||||
.nrvers = ARRAY_SIZE(nfslocalio_version),
|
||||
.version = nfslocalio_version,
|
||||
.stats = &nfslocalio_rpcstat,
|
||||
};
|
||||
|
||||
/*
|
||||
* nfs_local_enable - enable local i/o for an nfs_client
|
||||
*/
|
||||
static void nfs_local_enable(struct nfs_client *clp)
|
||||
{
|
||||
spin_lock(&clp->cl_localio_lock);
|
||||
set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
|
||||
trace_nfs_local_enable(clp);
|
||||
spin_unlock(&clp->cl_localio_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs_local_disable - disable local i/o for an nfs_client
|
||||
*/
|
||||
void nfs_local_disable(struct nfs_client *clp)
|
||||
{
|
||||
spin_lock(&clp->cl_localio_lock);
|
||||
if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
|
||||
trace_nfs_local_disable(clp);
|
||||
nfs_uuid_invalidate_one_client(&clp->cl_uuid);
|
||||
}
|
||||
spin_unlock(&clp->cl_localio_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs_init_localioclient - Initialise an NFS localio client connection
|
||||
*/
|
||||
static struct rpc_clnt *nfs_init_localioclient(struct nfs_client *clp)
|
||||
{
|
||||
struct rpc_clnt *rpcclient_localio;
|
||||
|
||||
rpcclient_localio = rpc_bind_new_program(clp->cl_rpcclient,
|
||||
&nfslocalio_program, 1);
|
||||
|
||||
dprintk_rcu("%s: server (%s) %s NFS LOCALIO.\n",
|
||||
__func__, rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
|
||||
(IS_ERR(rpcclient_localio) ? "does not support" : "supports"));
|
||||
|
||||
return rpcclient_localio;
|
||||
}
|
||||
|
||||
static bool nfs_server_uuid_is_local(struct nfs_client *clp)
|
||||
{
|
||||
u8 uuid[UUID_SIZE];
|
||||
struct rpc_message msg = {
|
||||
.rpc_argp = &uuid,
|
||||
};
|
||||
struct rpc_clnt *rpcclient_localio;
|
||||
int status;
|
||||
|
||||
rpcclient_localio = nfs_init_localioclient(clp);
|
||||
if (IS_ERR(rpcclient_localio))
|
||||
return false;
|
||||
|
||||
export_uuid(uuid, &clp->cl_uuid.uuid);
|
||||
|
||||
msg.rpc_proc = &nfs_localio_procedures[LOCALIOPROC_UUID_IS_LOCAL];
|
||||
status = rpc_call_sync(rpcclient_localio, &msg, 0);
|
||||
dprintk("%s: NFS reply UUID_IS_LOCAL: status=%d\n",
|
||||
__func__, status);
|
||||
rpc_shutdown_client(rpcclient_localio);
|
||||
|
||||
/* Server is only local if it initialized required struct members */
|
||||
if (status || !clp->cl_uuid.net || !clp->cl_uuid.dom)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs_local_probe - probe local i/o support for an nfs_server and nfs_client
|
||||
* - called after alloc_client and init_client (so cl_rpcclient exists)
|
||||
* - this function is idempotent, it can be called for old or new clients
|
||||
*/
|
||||
void nfs_local_probe(struct nfs_client *clp)
|
||||
{
|
||||
/* Disallow localio if disabled via sysfs or AUTH_SYS isn't used */
|
||||
if (!localio_enabled ||
|
||||
clp->cl_rpcclient->cl_auth->au_flavor != RPC_AUTH_UNIX) {
|
||||
nfs_local_disable(clp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nfs_client_is_local(clp)) {
|
||||
/* If already enabled, disable and re-enable */
|
||||
nfs_local_disable(clp);
|
||||
}
|
||||
|
||||
nfs_uuid_begin(&clp->cl_uuid);
|
||||
if (nfs_server_uuid_is_local(clp))
|
||||
nfs_local_enable(clp);
|
||||
nfs_uuid_end(&clp->cl_uuid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_local_probe);
|
||||
|
||||
/*
|
||||
* nfs_local_open_fh - open a local filehandle in terms of nfsd_file
|
||||
*
|
||||
* Returns a pointer to a struct nfsd_file or NULL
|
||||
*/
|
||||
struct nfsd_file *
|
||||
nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
||||
struct nfs_fh *fh, const fmode_t mode)
|
||||
{
|
||||
struct nfsd_file *localio;
|
||||
int status;
|
||||
|
||||
if (!nfs_server_is_local(clp))
|
||||
return NULL;
|
||||
if (mode & ~(FMODE_READ | FMODE_WRITE))
|
||||
return NULL;
|
||||
|
||||
localio = nfs_open_local_fh(&clp->cl_uuid, clp->cl_rpcclient,
|
||||
cred, fh, mode);
|
||||
if (IS_ERR(localio)) {
|
||||
status = PTR_ERR(localio);
|
||||
trace_nfs_local_open_fh(fh, mode, status);
|
||||
switch (status) {
|
||||
case -ENOMEM:
|
||||
case -ENXIO:
|
||||
case -ENOENT:
|
||||
/* Revalidate localio, will disable if unsupported */
|
||||
nfs_local_probe(clp);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return localio;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_local_open_fh);
|
||||
|
||||
static struct bio_vec *
|
||||
nfs_bvec_alloc_and_import_pagevec(struct page **pagevec,
|
||||
unsigned int npages, gfp_t flags)
|
||||
{
|
||||
struct bio_vec *bvec, *p;
|
||||
|
||||
bvec = kmalloc_array(npages, sizeof(*bvec), flags);
|
||||
if (bvec != NULL) {
|
||||
for (p = bvec; npages > 0; p++, pagevec++, npages--) {
|
||||
p->bv_page = *pagevec;
|
||||
p->bv_len = PAGE_SIZE;
|
||||
p->bv_offset = 0;
|
||||
}
|
||||
}
|
||||
return bvec;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
|
||||
{
|
||||
kfree(iocb->bvec);
|
||||
kfree(iocb);
|
||||
}
|
||||
|
||||
static struct nfs_local_kiocb *
|
||||
nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
|
||||
struct nfsd_file *localio, gfp_t flags)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb;
|
||||
|
||||
iocb = kmalloc(sizeof(*iocb), flags);
|
||||
if (iocb == NULL)
|
||||
return NULL;
|
||||
iocb->bvec = nfs_bvec_alloc_and_import_pagevec(hdr->page_array.pagevec,
|
||||
hdr->page_array.npages, flags);
|
||||
if (iocb->bvec == NULL) {
|
||||
kfree(iocb);
|
||||
return NULL;
|
||||
}
|
||||
init_sync_kiocb(&iocb->kiocb, nfs_to->nfsd_file_file(localio));
|
||||
iocb->kiocb.ki_pos = hdr->args.offset;
|
||||
iocb->localio = localio;
|
||||
iocb->hdr = hdr;
|
||||
iocb->kiocb.ki_flags &= ~IOCB_APPEND;
|
||||
return iocb;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_iter_init(struct iov_iter *i, struct nfs_local_kiocb *iocb, int dir)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
|
||||
iov_iter_bvec(i, dir, iocb->bvec, hdr->page_array.npages,
|
||||
hdr->args.count + hdr->args.pgbase);
|
||||
if (hdr->args.pgbase != 0)
|
||||
iov_iter_advance(i, hdr->args.pgbase);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_hdr_release(struct nfs_pgio_header *hdr,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
call_ops->rpc_call_done(&hdr->task, hdr);
|
||||
call_ops->rpc_release(hdr);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_pgio_init(struct nfs_pgio_header *hdr,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
hdr->task.tk_ops = call_ops;
|
||||
if (!hdr->task.tk_start)
|
||||
hdr->task.tk_start = ktime_get();
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_pgio_done(struct nfs_pgio_header *hdr, long status)
|
||||
{
|
||||
if (status >= 0) {
|
||||
hdr->res.count = status;
|
||||
hdr->res.op_status = NFS4_OK;
|
||||
hdr->task.tk_status = 0;
|
||||
} else {
|
||||
hdr->res.op_status = nfs4_stat_to_errno(status);
|
||||
hdr->task.tk_status = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
|
||||
nfs_to->nfsd_file_put_local(iocb->localio);
|
||||
nfs_local_iocb_free(iocb);
|
||||
nfs_local_hdr_release(hdr, hdr->task.tk_ops);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
struct file *filp = iocb->kiocb.ki_filp;
|
||||
|
||||
nfs_local_pgio_done(hdr, status);
|
||||
|
||||
if (hdr->res.count != hdr->args.count ||
|
||||
hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp)))
|
||||
hdr->res.eof = true;
|
||||
|
||||
dprintk("%s: read %ld bytes eof %d.\n", __func__,
|
||||
status > 0 ? status : 0, hdr->res.eof);
|
||||
}
|
||||
|
||||
static void nfs_local_call_read(struct work_struct *work)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb =
|
||||
container_of(work, struct nfs_local_kiocb, work);
|
||||
struct file *filp = iocb->kiocb.ki_filp;
|
||||
const struct cred *save_cred;
|
||||
struct iov_iter iter;
|
||||
ssize_t status;
|
||||
|
||||
save_cred = override_creds(filp->f_cred);
|
||||
|
||||
nfs_local_iter_init(&iter, iocb, READ);
|
||||
|
||||
status = filp->f_op->read_iter(&iocb->kiocb, &iter);
|
||||
WARN_ON_ONCE(status == -EIOCBQUEUED);
|
||||
|
||||
nfs_local_read_done(iocb, status);
|
||||
nfs_local_pgio_release(iocb);
|
||||
|
||||
revert_creds(save_cred);
|
||||
}
|
||||
|
||||
static int
|
||||
nfs_do_local_read(struct nfs_pgio_header *hdr,
|
||||
struct nfsd_file *localio,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb;
|
||||
|
||||
dprintk("%s: vfs_read count=%u pos=%llu\n",
|
||||
__func__, hdr->args.count, hdr->args.offset);
|
||||
|
||||
iocb = nfs_local_iocb_alloc(hdr, localio, GFP_KERNEL);
|
||||
if (iocb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
nfs_local_pgio_init(hdr, call_ops);
|
||||
hdr->res.eof = false;
|
||||
|
||||
INIT_WORK(&iocb->work, nfs_local_call_read);
|
||||
queue_work(nfslocaliod_workqueue, &iocb->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_copy_boot_verifier(struct nfs_write_verifier *verifier, struct inode *inode)
|
||||
{
|
||||
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
|
||||
u32 *verf = (u32 *)verifier->data;
|
||||
int seq = 0;
|
||||
|
||||
do {
|
||||
read_seqbegin_or_lock(&clp->cl_boot_lock, &seq);
|
||||
verf[0] = (u32)clp->cl_nfssvc_boot.tv_sec;
|
||||
verf[1] = (u32)clp->cl_nfssvc_boot.tv_nsec;
|
||||
} while (need_seqretry(&clp->cl_boot_lock, seq));
|
||||
done_seqretry(&clp->cl_boot_lock, seq);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_reset_boot_verifier(struct inode *inode)
|
||||
{
|
||||
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
|
||||
|
||||
write_seqlock(&clp->cl_boot_lock);
|
||||
ktime_get_real_ts64(&clp->cl_nfssvc_boot);
|
||||
write_sequnlock(&clp->cl_boot_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_set_local_verifier(struct inode *inode,
|
||||
struct nfs_writeverf *verf,
|
||||
enum nfs3_stable_how how)
|
||||
{
|
||||
nfs_copy_boot_verifier(&verf->verifier, inode);
|
||||
verf->committed = how;
|
||||
}
|
||||
|
||||
/* Factored out from fs/nfsd/vfs.h:fh_getattr() */
|
||||
static int __vfs_getattr(struct path *p, struct kstat *stat, int version)
|
||||
{
|
||||
u32 request_mask = STATX_BASIC_STATS;
|
||||
|
||||
if (version == 4)
|
||||
request_mask |= (STATX_BTIME | STATX_CHANGE_COOKIE);
|
||||
return vfs_getattr(p, stat, request_mask, AT_STATX_SYNC_AS_STAT);
|
||||
}
|
||||
|
||||
/* Copied from fs/nfsd/nfsfh.c:nfsd4_change_attribute() */
|
||||
static u64 __nfsd4_change_attribute(const struct kstat *stat,
|
||||
const struct inode *inode)
|
||||
{
|
||||
u64 chattr;
|
||||
|
||||
if (stat->result_mask & STATX_CHANGE_COOKIE) {
|
||||
chattr = stat->change_cookie;
|
||||
if (S_ISREG(inode->i_mode) &&
|
||||
!(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) {
|
||||
chattr += (u64)stat->ctime.tv_sec << 30;
|
||||
chattr += stat->ctime.tv_nsec;
|
||||
}
|
||||
} else {
|
||||
chattr = time_to_chattr(&stat->ctime);
|
||||
}
|
||||
return chattr;
|
||||
}
|
||||
|
||||
static void nfs_local_vfs_getattr(struct nfs_local_kiocb *iocb)
|
||||
{
|
||||
struct kstat stat;
|
||||
struct file *filp = iocb->kiocb.ki_filp;
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
struct nfs_fattr *fattr = hdr->res.fattr;
|
||||
int version = NFS_PROTO(hdr->inode)->version;
|
||||
|
||||
if (unlikely(!fattr) || __vfs_getattr(&filp->f_path, &stat, version))
|
||||
return;
|
||||
|
||||
fattr->valid = (NFS_ATTR_FATTR_FILEID |
|
||||
NFS_ATTR_FATTR_CHANGE |
|
||||
NFS_ATTR_FATTR_SIZE |
|
||||
NFS_ATTR_FATTR_ATIME |
|
||||
NFS_ATTR_FATTR_MTIME |
|
||||
NFS_ATTR_FATTR_CTIME |
|
||||
NFS_ATTR_FATTR_SPACE_USED);
|
||||
|
||||
fattr->fileid = stat.ino;
|
||||
fattr->size = stat.size;
|
||||
fattr->atime = stat.atime;
|
||||
fattr->mtime = stat.mtime;
|
||||
fattr->ctime = stat.ctime;
|
||||
if (version == 4) {
|
||||
fattr->change_attr =
|
||||
__nfsd4_change_attribute(&stat, file_inode(filp));
|
||||
} else
|
||||
fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
|
||||
fattr->du.nfs3.used = stat.blocks << 9;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
struct inode *inode = hdr->inode;
|
||||
|
||||
dprintk("%s: wrote %ld bytes.\n", __func__, status > 0 ? status : 0);
|
||||
|
||||
/* Handle short writes as if they are ENOSPC */
|
||||
if (status > 0 && status < hdr->args.count) {
|
||||
hdr->mds_offset += status;
|
||||
hdr->args.offset += status;
|
||||
hdr->args.pgbase += status;
|
||||
hdr->args.count -= status;
|
||||
nfs_set_pgio_error(hdr, -ENOSPC, hdr->args.offset);
|
||||
status = -ENOSPC;
|
||||
}
|
||||
if (status < 0)
|
||||
nfs_reset_boot_verifier(inode);
|
||||
else if (nfs_should_remove_suid(inode)) {
|
||||
/* Deal with the suid/sgid bit corner case */
|
||||
spin_lock(&inode->i_lock);
|
||||
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MODE);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
nfs_local_pgio_done(hdr, status);
|
||||
}
|
||||
|
||||
static void nfs_local_call_write(struct work_struct *work)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb =
|
||||
container_of(work, struct nfs_local_kiocb, work);
|
||||
struct file *filp = iocb->kiocb.ki_filp;
|
||||
unsigned long old_flags = current->flags;
|
||||
const struct cred *save_cred;
|
||||
struct iov_iter iter;
|
||||
ssize_t status;
|
||||
|
||||
current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
|
||||
save_cred = override_creds(filp->f_cred);
|
||||
|
||||
nfs_local_iter_init(&iter, iocb, WRITE);
|
||||
|
||||
file_start_write(filp);
|
||||
status = filp->f_op->write_iter(&iocb->kiocb, &iter);
|
||||
file_end_write(filp);
|
||||
WARN_ON_ONCE(status == -EIOCBQUEUED);
|
||||
|
||||
nfs_local_write_done(iocb, status);
|
||||
nfs_local_vfs_getattr(iocb);
|
||||
nfs_local_pgio_release(iocb);
|
||||
|
||||
revert_creds(save_cred);
|
||||
current->flags = old_flags;
|
||||
}
|
||||
|
||||
static int
|
||||
nfs_do_local_write(struct nfs_pgio_header *hdr,
|
||||
struct nfsd_file *localio,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb;
|
||||
|
||||
dprintk("%s: vfs_write count=%u pos=%llu %s\n",
|
||||
__func__, hdr->args.count, hdr->args.offset,
|
||||
(hdr->args.stable == NFS_UNSTABLE) ? "unstable" : "stable");
|
||||
|
||||
iocb = nfs_local_iocb_alloc(hdr, localio, GFP_NOIO);
|
||||
if (iocb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (hdr->args.stable) {
|
||||
default:
|
||||
break;
|
||||
case NFS_DATA_SYNC:
|
||||
iocb->kiocb.ki_flags |= IOCB_DSYNC;
|
||||
break;
|
||||
case NFS_FILE_SYNC:
|
||||
iocb->kiocb.ki_flags |= IOCB_DSYNC|IOCB_SYNC;
|
||||
}
|
||||
nfs_local_pgio_init(hdr, call_ops);
|
||||
|
||||
nfs_set_local_verifier(hdr->inode, hdr->res.verf, hdr->args.stable);
|
||||
|
||||
INIT_WORK(&iocb->work, nfs_local_call_write);
|
||||
queue_work(nfslocaliod_workqueue, &iocb->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
|
||||
struct nfs_pgio_header *hdr,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
int status = 0;
|
||||
struct file *filp = nfs_to->nfsd_file_file(localio);
|
||||
|
||||
if (!hdr->args.count)
|
||||
return 0;
|
||||
/* Don't support filesystems without read_iter/write_iter */
|
||||
if (!filp->f_op->read_iter || !filp->f_op->write_iter) {
|
||||
nfs_local_disable(clp);
|
||||
status = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (hdr->rw_mode) {
|
||||
case FMODE_READ:
|
||||
status = nfs_do_local_read(hdr, localio, call_ops);
|
||||
break;
|
||||
case FMODE_WRITE:
|
||||
status = nfs_do_local_write(hdr, localio, call_ops);
|
||||
break;
|
||||
default:
|
||||
dprintk("%s: invalid mode: %d\n", __func__,
|
||||
hdr->rw_mode);
|
||||
status = -EINVAL;
|
||||
}
|
||||
out:
|
||||
if (status != 0) {
|
||||
nfs_to->nfsd_file_put_local(localio);
|
||||
hdr->task.tk_status = status;
|
||||
nfs_local_hdr_release(hdr, call_ops);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_init_commit(struct nfs_commit_data *data,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
data->task.tk_ops = call_ops;
|
||||
}
|
||||
|
||||
static int
|
||||
nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
|
||||
{
|
||||
loff_t start = data->args.offset;
|
||||
loff_t end = LLONG_MAX;
|
||||
|
||||
if (data->args.count > 0) {
|
||||
end = start + data->args.count - 1;
|
||||
if (end < start)
|
||||
end = LLONG_MAX;
|
||||
}
|
||||
|
||||
dprintk("%s: commit %llu - %llu\n", __func__, start, end);
|
||||
return vfs_fsync_range(filp, start, end, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_commit_done(struct nfs_commit_data *data, int status)
|
||||
{
|
||||
if (status >= 0) {
|
||||
nfs_set_local_verifier(data->inode,
|
||||
data->res.verf,
|
||||
NFS_FILE_SYNC);
|
||||
data->res.op_status = NFS4_OK;
|
||||
data->task.tk_status = 0;
|
||||
} else {
|
||||
nfs_reset_boot_verifier(data->inode);
|
||||
data->res.op_status = nfs4_stat_to_errno(status);
|
||||
data->task.tk_status = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_release_commit_data(struct nfsd_file *localio,
|
||||
struct nfs_commit_data *data,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
nfs_to->nfsd_file_put_local(localio);
|
||||
call_ops->rpc_call_done(&data->task, data);
|
||||
call_ops->rpc_release(data);
|
||||
}
|
||||
|
||||
static struct nfs_local_fsync_ctx *
|
||||
nfs_local_fsync_ctx_alloc(struct nfs_commit_data *data,
|
||||
struct nfsd_file *localio, gfp_t flags)
|
||||
{
|
||||
struct nfs_local_fsync_ctx *ctx = kmalloc(sizeof(*ctx), flags);
|
||||
|
||||
if (ctx != NULL) {
|
||||
ctx->localio = localio;
|
||||
ctx->data = data;
|
||||
INIT_WORK(&ctx->work, nfs_local_fsync_work);
|
||||
kref_init(&ctx->kref);
|
||||
ctx->done = NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_fsync_ctx_kref_free(struct kref *kref)
|
||||
{
|
||||
kfree(container_of(kref, struct nfs_local_fsync_ctx, kref));
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_fsync_ctx_put(struct nfs_local_fsync_ctx *ctx)
|
||||
{
|
||||
kref_put(&ctx->kref, nfs_local_fsync_ctx_kref_free);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_fsync_ctx_free(struct nfs_local_fsync_ctx *ctx)
|
||||
{
|
||||
nfs_local_release_commit_data(ctx->localio, ctx->data,
|
||||
ctx->data->task.tk_ops);
|
||||
nfs_local_fsync_ctx_put(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_fsync_work(struct work_struct *work)
|
||||
{
|
||||
struct nfs_local_fsync_ctx *ctx;
|
||||
int status;
|
||||
|
||||
ctx = container_of(work, struct nfs_local_fsync_ctx, work);
|
||||
|
||||
status = nfs_local_run_commit(nfs_to->nfsd_file_file(ctx->localio),
|
||||
ctx->data);
|
||||
nfs_local_commit_done(ctx->data, status);
|
||||
if (ctx->done != NULL)
|
||||
complete(ctx->done);
|
||||
nfs_local_fsync_ctx_free(ctx);
|
||||
}
|
||||
|
||||
int nfs_local_commit(struct nfsd_file *localio,
|
||||
struct nfs_commit_data *data,
|
||||
const struct rpc_call_ops *call_ops, int how)
|
||||
{
|
||||
struct nfs_local_fsync_ctx *ctx;
|
||||
|
||||
ctx = nfs_local_fsync_ctx_alloc(data, localio, GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
nfs_local_commit_done(data, -ENOMEM);
|
||||
nfs_local_release_commit_data(localio, data, call_ops);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nfs_local_init_commit(data, call_ops);
|
||||
kref_get(&ctx->kref);
|
||||
if (how & FLUSH_SYNC) {
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
ctx->done = &done;
|
||||
queue_work(nfsiod_workqueue, &ctx->work);
|
||||
wait_for_completion(&done);
|
||||
} else
|
||||
queue_work(nfsiod_workqueue, &ctx->work);
|
||||
nfs_local_fsync_ctx_put(ctx);
|
||||
return 0;
|
||||
}
|
@ -22,14 +22,12 @@
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs2.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_common.h>
|
||||
#include "nfstrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_XDR
|
||||
|
||||
/* Mapping from NFS error code to "errno" error code. */
|
||||
#define errno_NFSERR_IO EIO
|
||||
|
||||
/*
|
||||
* Declare the space requirements for NFS arguments and replies as
|
||||
* number of 32bit-words
|
||||
@ -64,8 +62,6 @@
|
||||
#define NFS_readdirres_sz (1+NFS_pagepad_sz)
|
||||
#define NFS_statfsres_sz (1+NFS_info_sz)
|
||||
|
||||
static int nfs_stat_to_errno(enum nfs_stat);
|
||||
|
||||
/*
|
||||
* Encode/decode NFSv2 basic data types
|
||||
*
|
||||
@ -1054,70 +1050,6 @@ out_default:
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We need to translate between nfs status return values and
|
||||
* the local errno values which may not be the same.
|
||||
*/
|
||||
static const struct {
|
||||
int stat;
|
||||
int errno;
|
||||
} nfs_errtbl[] = {
|
||||
{ NFS_OK, 0 },
|
||||
{ NFSERR_PERM, -EPERM },
|
||||
{ NFSERR_NOENT, -ENOENT },
|
||||
{ NFSERR_IO, -errno_NFSERR_IO},
|
||||
{ NFSERR_NXIO, -ENXIO },
|
||||
/* { NFSERR_EAGAIN, -EAGAIN }, */
|
||||
{ NFSERR_ACCES, -EACCES },
|
||||
{ NFSERR_EXIST, -EEXIST },
|
||||
{ NFSERR_XDEV, -EXDEV },
|
||||
{ NFSERR_NODEV, -ENODEV },
|
||||
{ NFSERR_NOTDIR, -ENOTDIR },
|
||||
{ NFSERR_ISDIR, -EISDIR },
|
||||
{ NFSERR_INVAL, -EINVAL },
|
||||
{ NFSERR_FBIG, -EFBIG },
|
||||
{ NFSERR_NOSPC, -ENOSPC },
|
||||
{ NFSERR_ROFS, -EROFS },
|
||||
{ NFSERR_MLINK, -EMLINK },
|
||||
{ NFSERR_NAMETOOLONG, -ENAMETOOLONG },
|
||||
{ NFSERR_NOTEMPTY, -ENOTEMPTY },
|
||||
{ NFSERR_DQUOT, -EDQUOT },
|
||||
{ NFSERR_STALE, -ESTALE },
|
||||
{ NFSERR_REMOTE, -EREMOTE },
|
||||
#ifdef EWFLUSH
|
||||
{ NFSERR_WFLUSH, -EWFLUSH },
|
||||
#endif
|
||||
{ NFSERR_BADHANDLE, -EBADHANDLE },
|
||||
{ NFSERR_NOT_SYNC, -ENOTSYNC },
|
||||
{ NFSERR_BAD_COOKIE, -EBADCOOKIE },
|
||||
{ NFSERR_NOTSUPP, -ENOTSUPP },
|
||||
{ NFSERR_TOOSMALL, -ETOOSMALL },
|
||||
{ NFSERR_SERVERFAULT, -EREMOTEIO },
|
||||
{ NFSERR_BADTYPE, -EBADTYPE },
|
||||
{ NFSERR_JUKEBOX, -EJUKEBOX },
|
||||
{ -1, -EIO }
|
||||
};
|
||||
|
||||
/**
|
||||
* nfs_stat_to_errno - convert an NFS status code to a local errno
|
||||
* @status: NFS status code to convert
|
||||
*
|
||||
* Returns a local errno value, or -EIO if the NFS status code is
|
||||
* not recognized. This function is used jointly by NFSv2 and NFSv3.
|
||||
*/
|
||||
static int nfs_stat_to_errno(enum nfs_stat status)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
|
||||
if (nfs_errtbl[i].stat == (int)status)
|
||||
return nfs_errtbl[i].errno;
|
||||
}
|
||||
dprintk("NFS: Unrecognized nfs status value: %u\n", status);
|
||||
return nfs_errtbl[i].errno;
|
||||
}
|
||||
|
||||
#define PROC(proc, argtype, restype, timer) \
|
||||
[NFSPROC_##proc] = { \
|
||||
.p_proc = NFSPROC_##proc, \
|
||||
|
108
fs/nfs/nfs3xdr.c
108
fs/nfs/nfs3xdr.c
@ -21,14 +21,13 @@
|
||||
#include <linux/nfs3.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfsacl.h>
|
||||
#include <linux/nfs_common.h>
|
||||
|
||||
#include "nfstrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_XDR
|
||||
|
||||
/* Mapping from NFS error code to "errno" error code. */
|
||||
#define errno_NFSERR_IO EIO
|
||||
|
||||
/*
|
||||
* Declare the space requirements for NFS arguments and replies as
|
||||
* number of 32bit-words
|
||||
@ -91,8 +90,6 @@
|
||||
NFS3_pagepad_sz)
|
||||
#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
|
||||
|
||||
static int nfs3_stat_to_errno(enum nfs_stat);
|
||||
|
||||
/*
|
||||
* Map file type to S_IFMT bits
|
||||
*/
|
||||
@ -1406,7 +1403,7 @@ static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_default:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1445,7 +1442,7 @@ static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1495,7 +1492,7 @@ out_default:
|
||||
error = decode_post_op_attr(xdr, result->dir_attr, userns);
|
||||
if (unlikely(error))
|
||||
goto out;
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1537,7 +1534,7 @@ static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_default:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1578,7 +1575,7 @@ static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_default:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1658,7 +1655,7 @@ static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1728,7 +1725,7 @@ static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1795,7 +1792,7 @@ out_default:
|
||||
error = decode_wcc_data(xdr, result->dir_attr, userns);
|
||||
if (unlikely(error))
|
||||
goto out;
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1835,7 +1832,7 @@ static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1881,7 +1878,7 @@ static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1926,7 +1923,7 @@ static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2101,7 +2098,7 @@ out_default:
|
||||
error = decode_post_op_attr(xdr, result->dir_attr, rpc_rqst_userns(req));
|
||||
if (unlikely(error))
|
||||
goto out;
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2167,7 +2164,7 @@ static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2243,7 +2240,7 @@ static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2304,7 +2301,7 @@ static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2350,7 +2347,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_status:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFS_V3_ACL
|
||||
@ -2416,7 +2413,7 @@ static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_default:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
|
||||
@ -2435,76 +2432,11 @@ static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
|
||||
out:
|
||||
return error;
|
||||
out_default:
|
||||
return nfs3_stat_to_errno(status);
|
||||
return nfs_stat_to_errno(status);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NFS_V3_ACL */
|
||||
|
||||
|
||||
/*
|
||||
* We need to translate between nfs status return values and
|
||||
* the local errno values which may not be the same.
|
||||
*/
|
||||
static const struct {
|
||||
int stat;
|
||||
int errno;
|
||||
} nfs_errtbl[] = {
|
||||
{ NFS_OK, 0 },
|
||||
{ NFSERR_PERM, -EPERM },
|
||||
{ NFSERR_NOENT, -ENOENT },
|
||||
{ NFSERR_IO, -errno_NFSERR_IO},
|
||||
{ NFSERR_NXIO, -ENXIO },
|
||||
/* { NFSERR_EAGAIN, -EAGAIN }, */
|
||||
{ NFSERR_ACCES, -EACCES },
|
||||
{ NFSERR_EXIST, -EEXIST },
|
||||
{ NFSERR_XDEV, -EXDEV },
|
||||
{ NFSERR_NODEV, -ENODEV },
|
||||
{ NFSERR_NOTDIR, -ENOTDIR },
|
||||
{ NFSERR_ISDIR, -EISDIR },
|
||||
{ NFSERR_INVAL, -EINVAL },
|
||||
{ NFSERR_FBIG, -EFBIG },
|
||||
{ NFSERR_NOSPC, -ENOSPC },
|
||||
{ NFSERR_ROFS, -EROFS },
|
||||
{ NFSERR_MLINK, -EMLINK },
|
||||
{ NFSERR_NAMETOOLONG, -ENAMETOOLONG },
|
||||
{ NFSERR_NOTEMPTY, -ENOTEMPTY },
|
||||
{ NFSERR_DQUOT, -EDQUOT },
|
||||
{ NFSERR_STALE, -ESTALE },
|
||||
{ NFSERR_REMOTE, -EREMOTE },
|
||||
#ifdef EWFLUSH
|
||||
{ NFSERR_WFLUSH, -EWFLUSH },
|
||||
#endif
|
||||
{ NFSERR_BADHANDLE, -EBADHANDLE },
|
||||
{ NFSERR_NOT_SYNC, -ENOTSYNC },
|
||||
{ NFSERR_BAD_COOKIE, -EBADCOOKIE },
|
||||
{ NFSERR_NOTSUPP, -ENOTSUPP },
|
||||
{ NFSERR_TOOSMALL, -ETOOSMALL },
|
||||
{ NFSERR_SERVERFAULT, -EREMOTEIO },
|
||||
{ NFSERR_BADTYPE, -EBADTYPE },
|
||||
{ NFSERR_JUKEBOX, -EJUKEBOX },
|
||||
{ -1, -EIO }
|
||||
};
|
||||
|
||||
/**
|
||||
* nfs3_stat_to_errno - convert an NFS status code to a local errno
|
||||
* @status: NFS status code to convert
|
||||
*
|
||||
* Returns a local errno value, or -EIO if the NFS status code is
|
||||
* not recognized. This function is used jointly by NFSv2 and NFSv3.
|
||||
*/
|
||||
static int nfs3_stat_to_errno(enum nfs_stat status)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
|
||||
if (nfs_errtbl[i].stat == (int)status)
|
||||
return nfs_errtbl[i].errno;
|
||||
}
|
||||
dprintk("NFS: Unrecognized nfs status value: %u\n", status);
|
||||
return nfs_errtbl[i].errno;
|
||||
}
|
||||
|
||||
|
||||
#define PROC(proc, argtype, restype, timer) \
|
||||
[NFS3PROC_##proc] = { \
|
||||
.p_proc = NFS3PROC_##proc, \
|
||||
|
@ -83,7 +83,7 @@ struct nfs4_minor_version_ops {
|
||||
#define NFS_SEQID_CONFIRMED 1
|
||||
struct nfs_seqid_counter {
|
||||
ktime_t create_time;
|
||||
int owner_id;
|
||||
u64 owner_id;
|
||||
int flags;
|
||||
u32 counter;
|
||||
spinlock_t lock; /* Protects the list */
|
||||
|
@ -3904,6 +3904,18 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
|
||||
#define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL)
|
||||
#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_OPEN_ARGUMENTS - 1UL)
|
||||
|
||||
#define FATTR4_WORD2_NFS42_TIME_DELEG_MASK \
|
||||
(FATTR4_WORD2_TIME_DELEG_MODIFY|FATTR4_WORD2_TIME_DELEG_ACCESS)
|
||||
static bool nfs4_server_delegtime_capable(struct nfs4_server_caps_res *res)
|
||||
{
|
||||
u32 share_access_want = res->open_caps.oa_share_access_want[0];
|
||||
u32 attr_bitmask = res->attr_bitmask[2];
|
||||
|
||||
return (share_access_want & NFS4_SHARE_WANT_DELEG_TIMESTAMPS) &&
|
||||
((attr_bitmask & FATTR4_WORD2_NFS42_TIME_DELEG_MASK) ==
|
||||
FATTR4_WORD2_NFS42_TIME_DELEG_MASK);
|
||||
}
|
||||
|
||||
static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
|
||||
{
|
||||
u32 minorversion = server->nfs_client->cl_minorversion;
|
||||
@ -3982,8 +3994,6 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
||||
#endif
|
||||
if (res.attr_bitmask[0] & FATTR4_WORD0_FS_LOCATIONS)
|
||||
server->caps |= NFS_CAP_FS_LOCATIONS;
|
||||
if (res.attr_bitmask[2] & FATTR4_WORD2_TIME_DELEG_MODIFY)
|
||||
server->caps |= NFS_CAP_DELEGTIME;
|
||||
if (!(res.attr_bitmask[0] & FATTR4_WORD0_FILEID))
|
||||
server->fattr_valid &= ~NFS_ATTR_FATTR_FILEID;
|
||||
if (!(res.attr_bitmask[1] & FATTR4_WORD1_MODE))
|
||||
@ -4011,6 +4021,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
||||
if (res.open_caps.oa_share_access_want[0] &
|
||||
NFS4_SHARE_WANT_OPEN_XOR_DELEGATION)
|
||||
server->caps |= NFS_CAP_OPEN_XOR;
|
||||
if (nfs4_server_delegtime_capable(&res))
|
||||
server->caps |= NFS_CAP_DELEGTIME;
|
||||
|
||||
memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
|
||||
server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
|
||||
|
@ -501,11 +501,7 @@ nfs4_alloc_state_owner(struct nfs_server *server,
|
||||
sp = kzalloc(sizeof(*sp), gfp_flags);
|
||||
if (!sp)
|
||||
return NULL;
|
||||
sp->so_seqid.owner_id = ida_alloc(&server->openowner_id, gfp_flags);
|
||||
if (sp->so_seqid.owner_id < 0) {
|
||||
kfree(sp);
|
||||
return NULL;
|
||||
}
|
||||
sp->so_seqid.owner_id = atomic64_inc_return(&server->owner_ctr);
|
||||
sp->so_server = server;
|
||||
sp->so_cred = get_cred(cred);
|
||||
spin_lock_init(&sp->so_lock);
|
||||
@ -536,7 +532,6 @@ static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
|
||||
{
|
||||
nfs4_destroy_seqid_counter(&sp->so_seqid);
|
||||
put_cred(sp->so_cred);
|
||||
ida_free(&sp->so_server->openowner_id, sp->so_seqid.owner_id);
|
||||
kfree(sp);
|
||||
}
|
||||
|
||||
@ -879,19 +874,13 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
|
||||
refcount_set(&lsp->ls_count, 1);
|
||||
lsp->ls_state = state;
|
||||
lsp->ls_owner = owner;
|
||||
lsp->ls_seqid.owner_id = ida_alloc(&server->lockowner_id, GFP_KERNEL_ACCOUNT);
|
||||
if (lsp->ls_seqid.owner_id < 0)
|
||||
goto out_free;
|
||||
lsp->ls_seqid.owner_id = atomic64_inc_return(&server->owner_ctr);
|
||||
INIT_LIST_HEAD(&lsp->ls_locks);
|
||||
return lsp;
|
||||
out_free:
|
||||
kfree(lsp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
|
||||
{
|
||||
ida_free(&server->lockowner_id, lsp->ls_seqid.owner_id);
|
||||
nfs4_destroy_seqid_counter(&lsp->ls_seqid);
|
||||
kfree(lsp);
|
||||
}
|
||||
@ -1957,6 +1946,7 @@ restart:
|
||||
set_bit(ops->owner_flag_bit, &sp->so_flags);
|
||||
nfs4_put_state_owner(sp);
|
||||
status = nfs4_recovery_handle_error(clp, status);
|
||||
nfs4_free_state_owners(&freeme);
|
||||
return (status != 0) ? status : -EAGAIN;
|
||||
}
|
||||
|
||||
@ -2023,6 +2013,12 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
|
||||
nfs_mark_client_ready(clp, -EPERM);
|
||||
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
||||
return -EPERM;
|
||||
case -ETIMEDOUT:
|
||||
if (clp->cl_cons_state == NFS_CS_SESSION_INITING) {
|
||||
nfs_mark_client_ready(clp, -EIO);
|
||||
return -EIO;
|
||||
}
|
||||
fallthrough;
|
||||
case -EACCES:
|
||||
case -NFS4ERR_DELAY:
|
||||
case -EAGAIN:
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs4.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_common.h>
|
||||
|
||||
#include "nfs4_fs.h"
|
||||
#include "nfs4trace.h"
|
||||
@ -63,11 +64,7 @@
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_XDR
|
||||
|
||||
/* Mapping from NFS error code to "errno" error code. */
|
||||
#define errno_NFSERR_IO EIO
|
||||
|
||||
struct compound_hdr;
|
||||
static int nfs4_stat_to_errno(int);
|
||||
static void encode_layoutget(struct xdr_stream *xdr,
|
||||
const struct nfs4_layoutget_args *args,
|
||||
struct compound_hdr *hdr);
|
||||
@ -975,11 +972,6 @@ static __be32 *reserve_space(struct xdr_stream *xdr, size_t nbytes)
|
||||
return p;
|
||||
}
|
||||
|
||||
static void encode_opaque_fixed(struct xdr_stream *xdr, const void *buf, size_t len)
|
||||
{
|
||||
WARN_ON_ONCE(xdr_stream_encode_opaque_fixed(xdr, buf, len) < 0);
|
||||
}
|
||||
|
||||
static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
|
||||
{
|
||||
WARN_ON_ONCE(xdr_stream_encode_opaque(xdr, str, len) < 0);
|
||||
@ -1424,12 +1416,12 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
|
||||
*/
|
||||
encode_nfs4_seqid(xdr, arg->seqid);
|
||||
encode_share_access(xdr, arg->share_access);
|
||||
p = reserve_space(xdr, 36);
|
||||
p = reserve_space(xdr, 40);
|
||||
p = xdr_encode_hyper(p, arg->clientid);
|
||||
*p++ = cpu_to_be32(24);
|
||||
*p++ = cpu_to_be32(28);
|
||||
p = xdr_encode_opaque_fixed(p, "open id:", 8);
|
||||
*p++ = cpu_to_be32(arg->server->s_dev);
|
||||
*p++ = cpu_to_be32(arg->id.uniquifier);
|
||||
p = xdr_encode_hyper(p, arg->id.uniquifier);
|
||||
xdr_encode_hyper(p, arg->id.create_time);
|
||||
}
|
||||
|
||||
@ -4408,14 +4400,6 @@ static int decode_access(struct xdr_stream *xdr, u32 *supported, u32 *access)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len)
|
||||
{
|
||||
ssize_t ret = xdr_stream_decode_opaque_fixed(xdr, buf, len);
|
||||
if (unlikely(ret < 0))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
|
||||
{
|
||||
return decode_opaque_fixed(xdr, stateid, NFS4_STATEID_SIZE);
|
||||
@ -7620,72 +7604,6 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to translate between nfs status return values and
|
||||
* the local errno values which may not be the same.
|
||||
*/
|
||||
static struct {
|
||||
int stat;
|
||||
int errno;
|
||||
} nfs_errtbl[] = {
|
||||
{ NFS4_OK, 0 },
|
||||
{ NFS4ERR_PERM, -EPERM },
|
||||
{ NFS4ERR_NOENT, -ENOENT },
|
||||
{ NFS4ERR_IO, -errno_NFSERR_IO},
|
||||
{ NFS4ERR_NXIO, -ENXIO },
|
||||
{ NFS4ERR_ACCESS, -EACCES },
|
||||
{ NFS4ERR_EXIST, -EEXIST },
|
||||
{ NFS4ERR_XDEV, -EXDEV },
|
||||
{ NFS4ERR_NOTDIR, -ENOTDIR },
|
||||
{ NFS4ERR_ISDIR, -EISDIR },
|
||||
{ NFS4ERR_INVAL, -EINVAL },
|
||||
{ NFS4ERR_FBIG, -EFBIG },
|
||||
{ NFS4ERR_NOSPC, -ENOSPC },
|
||||
{ NFS4ERR_ROFS, -EROFS },
|
||||
{ NFS4ERR_MLINK, -EMLINK },
|
||||
{ NFS4ERR_NAMETOOLONG, -ENAMETOOLONG },
|
||||
{ NFS4ERR_NOTEMPTY, -ENOTEMPTY },
|
||||
{ NFS4ERR_DQUOT, -EDQUOT },
|
||||
{ NFS4ERR_STALE, -ESTALE },
|
||||
{ NFS4ERR_BADHANDLE, -EBADHANDLE },
|
||||
{ NFS4ERR_BAD_COOKIE, -EBADCOOKIE },
|
||||
{ NFS4ERR_NOTSUPP, -ENOTSUPP },
|
||||
{ NFS4ERR_TOOSMALL, -ETOOSMALL },
|
||||
{ NFS4ERR_SERVERFAULT, -EREMOTEIO },
|
||||
{ NFS4ERR_BADTYPE, -EBADTYPE },
|
||||
{ NFS4ERR_LOCKED, -EAGAIN },
|
||||
{ NFS4ERR_SYMLINK, -ELOOP },
|
||||
{ NFS4ERR_OP_ILLEGAL, -EOPNOTSUPP },
|
||||
{ NFS4ERR_DEADLOCK, -EDEADLK },
|
||||
{ NFS4ERR_NOXATTR, -ENODATA },
|
||||
{ NFS4ERR_XATTR2BIG, -E2BIG },
|
||||
{ -1, -EIO }
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert an NFS error code to a local one.
|
||||
* This one is used jointly by NFSv2 and NFSv3.
|
||||
*/
|
||||
static int
|
||||
nfs4_stat_to_errno(int stat)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
|
||||
if (nfs_errtbl[i].stat == stat)
|
||||
return nfs_errtbl[i].errno;
|
||||
}
|
||||
if (stat <= 10000 || stat > 10100) {
|
||||
/* The server is looney tunes. */
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
/* If we cannot translate the error, the recovery routines should
|
||||
* handle it.
|
||||
* Note: remaining NFSv4 error codes have values > 10000, so should
|
||||
* not conflict with native Linux error codes.
|
||||
*/
|
||||
return -stat;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
#include "nfs42xdr.c"
|
||||
#endif /* CONFIG_NFS_V4_2 */
|
||||
|
@ -1685,6 +1685,67 @@ TRACE_EVENT(nfs_mount_path,
|
||||
TP_printk("path='%s'", __get_str(path))
|
||||
);
|
||||
|
||||
TRACE_EVENT(nfs_local_open_fh,
|
||||
TP_PROTO(
|
||||
const struct nfs_fh *fh,
|
||||
fmode_t fmode,
|
||||
int error
|
||||
),
|
||||
|
||||
TP_ARGS(fh, fmode, error),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(int, error)
|
||||
__field(u32, fhandle)
|
||||
__field(unsigned int, fmode)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->error = error;
|
||||
__entry->fhandle = nfs_fhandle_hash(fh);
|
||||
__entry->fmode = (__force unsigned int)fmode;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
"error=%d fhandle=0x%08x mode=%s",
|
||||
__entry->error,
|
||||
__entry->fhandle,
|
||||
show_fs_fmode_flags(__entry->fmode)
|
||||
)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs_local_client_event,
|
||||
TP_PROTO(
|
||||
const struct nfs_client *clp
|
||||
),
|
||||
|
||||
TP_ARGS(clp),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, protocol)
|
||||
__string(server, clp->cl_hostname)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->protocol = clp->rpc_ops->version;
|
||||
__assign_str(server);
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
"server=%s NFSv%u", __get_str(server), __entry->protocol
|
||||
)
|
||||
);
|
||||
|
||||
#define DEFINE_NFS_LOCAL_CLIENT_EVENT(name) \
|
||||
DEFINE_EVENT(nfs_local_client_event, name, \
|
||||
TP_PROTO( \
|
||||
const struct nfs_client *clp \
|
||||
), \
|
||||
TP_ARGS(clp))
|
||||
|
||||
DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_local_enable);
|
||||
DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_local_disable);
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs_xdr_event,
|
||||
TP_PROTO(
|
||||
const struct xdr_stream *xdr,
|
||||
|
@ -731,7 +731,8 @@ static void nfs_pgio_prepare(struct rpc_task *task, void *calldata)
|
||||
|
||||
int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
|
||||
const struct cred *cred, const struct nfs_rpc_ops *rpc_ops,
|
||||
const struct rpc_call_ops *call_ops, int how, int flags)
|
||||
const struct rpc_call_ops *call_ops, int how, int flags,
|
||||
struct nfsd_file *localio)
|
||||
{
|
||||
struct rpc_task *task;
|
||||
struct rpc_message msg = {
|
||||
@ -761,6 +762,10 @@ int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
|
||||
hdr->args.count,
|
||||
(unsigned long long)hdr->args.offset);
|
||||
|
||||
if (localio)
|
||||
return nfs_local_doio(NFS_SERVER(hdr->inode)->nfs_client,
|
||||
localio, hdr, call_ops);
|
||||
|
||||
task = rpc_run_task(&task_setup_data);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
@ -953,6 +958,12 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
|
||||
nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
|
||||
ret = nfs_generic_pgio(desc, hdr);
|
||||
if (ret == 0) {
|
||||
struct nfs_client *clp = NFS_SERVER(hdr->inode)->nfs_client;
|
||||
|
||||
struct nfsd_file *localio =
|
||||
nfs_local_open_fh(clp, hdr->cred,
|
||||
hdr->args.fh, hdr->args.context->mode);
|
||||
|
||||
if (NFS_SERVER(hdr->inode)->nfs_client->cl_minorversion)
|
||||
task_flags = RPC_TASK_MOVEABLE;
|
||||
ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode),
|
||||
@ -961,7 +972,8 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
|
||||
NFS_PROTO(hdr->inode),
|
||||
desc->pg_rpc_callops,
|
||||
desc->pg_ioflags,
|
||||
RPC_TASK_CRED_NOREF | task_flags);
|
||||
RPC_TASK_CRED_NOREF | task_flags,
|
||||
localio);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -490,7 +490,7 @@ pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
|
||||
nfs_initiate_commit(NFS_CLIENT(inode), data,
|
||||
NFS_PROTO(data->inode),
|
||||
data->mds_ops, how,
|
||||
RPC_TASK_CRED_NOREF);
|
||||
RPC_TASK_CRED_NOREF, NULL);
|
||||
} else {
|
||||
nfs_init_commit(data, NULL, data->lseg, cinfo);
|
||||
initiate_commit(data, how);
|
||||
|
@ -48,8 +48,7 @@ static struct nfs_pgio_header *nfs_readhdr_alloc(void)
|
||||
|
||||
static void nfs_readhdr_free(struct nfs_pgio_header *rhdr)
|
||||
{
|
||||
if (rhdr->res.scratch != NULL)
|
||||
kfree(rhdr->res.scratch);
|
||||
kfree(rhdr->res.scratch);
|
||||
kmem_cache_free(nfs_rdata_cachep, rhdr);
|
||||
}
|
||||
|
||||
|
@ -551,6 +551,9 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
|
||||
else
|
||||
seq_puts(m, ",local_lock=posix");
|
||||
|
||||
if (nfss->flags & NFS_MOUNT_NO_ALIGNWRITE)
|
||||
seq_puts(m, ",noalignwrite");
|
||||
|
||||
if (nfss->flags & NFS_MOUNT_WRITE_EAGER) {
|
||||
if (nfss->flags & NFS_MOUNT_WRITE_WAIT)
|
||||
seq_puts(m, ",write=wait");
|
||||
|
@ -772,8 +772,7 @@ static void nfs_inode_add_request(struct nfs_page *req)
|
||||
nfs_lock_request(req);
|
||||
spin_lock(&mapping->i_private_lock);
|
||||
set_bit(PG_MAPPED, &req->wb_flags);
|
||||
folio_set_private(folio);
|
||||
folio->private = req;
|
||||
folio_attach_private(folio, req);
|
||||
spin_unlock(&mapping->i_private_lock);
|
||||
atomic_long_inc(&nfsi->nrequests);
|
||||
/* this a head request for a page group - mark it as having an
|
||||
@ -797,8 +796,7 @@ static void nfs_inode_remove_request(struct nfs_page *req)
|
||||
|
||||
spin_lock(&mapping->i_private_lock);
|
||||
if (likely(folio)) {
|
||||
folio->private = NULL;
|
||||
folio_clear_private(folio);
|
||||
folio_detach_private(folio);
|
||||
clear_bit(PG_MAPPED, &req->wb_head->wb_flags);
|
||||
}
|
||||
spin_unlock(&mapping->i_private_lock);
|
||||
@ -1297,7 +1295,10 @@ static int nfs_can_extend_write(struct file *file, struct folio *folio,
|
||||
struct file_lock_context *flctx = locks_inode_context(inode);
|
||||
struct file_lock *fl;
|
||||
int ret;
|
||||
unsigned int mntflags = NFS_SERVER(inode)->flags;
|
||||
|
||||
if (mntflags & NFS_MOUNT_NO_ALIGNWRITE)
|
||||
return 0;
|
||||
if (file->f_flags & O_DSYNC)
|
||||
return 0;
|
||||
if (!nfs_folio_write_uptodate(folio, pagelen))
|
||||
@ -1663,7 +1664,8 @@ EXPORT_SYMBOL_GPL(nfs_commitdata_release);
|
||||
int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
|
||||
const struct nfs_rpc_ops *nfs_ops,
|
||||
const struct rpc_call_ops *call_ops,
|
||||
int how, int flags)
|
||||
int how, int flags,
|
||||
struct nfsd_file *localio)
|
||||
{
|
||||
struct rpc_task *task;
|
||||
int priority = flush_task_priority(how);
|
||||
@ -1692,6 +1694,9 @@ int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
|
||||
|
||||
dprintk("NFS: initiated commit call\n");
|
||||
|
||||
if (localio)
|
||||
return nfs_local_commit(localio, data, call_ops, how);
|
||||
|
||||
task = rpc_run_task(&task_setup_data);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
@ -1791,6 +1796,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
|
||||
struct nfs_commit_info *cinfo)
|
||||
{
|
||||
struct nfs_commit_data *data;
|
||||
struct nfsd_file *localio;
|
||||
unsigned short task_flags = 0;
|
||||
|
||||
/* another commit raced with us */
|
||||
@ -1807,9 +1813,12 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
|
||||
nfs_init_commit(data, head, NULL, cinfo);
|
||||
if (NFS_SERVER(inode)->nfs_client->cl_minorversion)
|
||||
task_flags = RPC_TASK_MOVEABLE;
|
||||
|
||||
localio = nfs_local_open_fh(NFS_SERVER(inode)->nfs_client, data->cred,
|
||||
data->args.fh, data->context->mode);
|
||||
return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
|
||||
data->mds_ops, how,
|
||||
RPC_TASK_CRED_NOREF | task_flags);
|
||||
RPC_TASK_CRED_NOREF | task_flags, localio);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -6,5 +6,10 @@
|
||||
obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
|
||||
nfs_acl-objs := nfsacl.o
|
||||
|
||||
obj-$(CONFIG_NFS_COMMON_LOCALIO_SUPPORT) += nfs_localio.o
|
||||
nfs_localio-objs := nfslocalio.o
|
||||
|
||||
obj-$(CONFIG_GRACE_PERIOD) += grace.o
|
||||
obj-$(CONFIG_NFS_V4_2_SSC_HELPER) += nfs_ssc.o
|
||||
|
||||
obj-$(CONFIG_NFS_COMMON) += common.o
|
||||
|
134
fs/nfs_common/common.c
Normal file
134
fs/nfs_common/common.c
Normal file
@ -0,0 +1,134 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/nfs_common.h>
|
||||
#include <linux/nfs4.h>
|
||||
|
||||
/*
|
||||
* We need to translate between nfs status return values and
|
||||
* the local errno values which may not be the same.
|
||||
*/
|
||||
static const struct {
|
||||
int stat;
|
||||
int errno;
|
||||
} nfs_errtbl[] = {
|
||||
{ NFS_OK, 0 },
|
||||
{ NFSERR_PERM, -EPERM },
|
||||
{ NFSERR_NOENT, -ENOENT },
|
||||
{ NFSERR_IO, -errno_NFSERR_IO},
|
||||
{ NFSERR_NXIO, -ENXIO },
|
||||
/* { NFSERR_EAGAIN, -EAGAIN }, */
|
||||
{ NFSERR_ACCES, -EACCES },
|
||||
{ NFSERR_EXIST, -EEXIST },
|
||||
{ NFSERR_XDEV, -EXDEV },
|
||||
{ NFSERR_NODEV, -ENODEV },
|
||||
{ NFSERR_NOTDIR, -ENOTDIR },
|
||||
{ NFSERR_ISDIR, -EISDIR },
|
||||
{ NFSERR_INVAL, -EINVAL },
|
||||
{ NFSERR_FBIG, -EFBIG },
|
||||
{ NFSERR_NOSPC, -ENOSPC },
|
||||
{ NFSERR_ROFS, -EROFS },
|
||||
{ NFSERR_MLINK, -EMLINK },
|
||||
{ NFSERR_NAMETOOLONG, -ENAMETOOLONG },
|
||||
{ NFSERR_NOTEMPTY, -ENOTEMPTY },
|
||||
{ NFSERR_DQUOT, -EDQUOT },
|
||||
{ NFSERR_STALE, -ESTALE },
|
||||
{ NFSERR_REMOTE, -EREMOTE },
|
||||
#ifdef EWFLUSH
|
||||
{ NFSERR_WFLUSH, -EWFLUSH },
|
||||
#endif
|
||||
{ NFSERR_BADHANDLE, -EBADHANDLE },
|
||||
{ NFSERR_NOT_SYNC, -ENOTSYNC },
|
||||
{ NFSERR_BAD_COOKIE, -EBADCOOKIE },
|
||||
{ NFSERR_NOTSUPP, -ENOTSUPP },
|
||||
{ NFSERR_TOOSMALL, -ETOOSMALL },
|
||||
{ NFSERR_SERVERFAULT, -EREMOTEIO },
|
||||
{ NFSERR_BADTYPE, -EBADTYPE },
|
||||
{ NFSERR_JUKEBOX, -EJUKEBOX },
|
||||
{ -1, -EIO }
|
||||
};
|
||||
|
||||
/**
|
||||
* nfs_stat_to_errno - convert an NFS status code to a local errno
|
||||
* @status: NFS status code to convert
|
||||
*
|
||||
* Returns a local errno value, or -EIO if the NFS status code is
|
||||
* not recognized. This function is used jointly by NFSv2 and NFSv3.
|
||||
*/
|
||||
int nfs_stat_to_errno(enum nfs_stat status)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
|
||||
if (nfs_errtbl[i].stat == (int)status)
|
||||
return nfs_errtbl[i].errno;
|
||||
}
|
||||
return nfs_errtbl[i].errno;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_stat_to_errno);
|
||||
|
||||
/*
|
||||
* We need to translate between nfs v4 status return values and
|
||||
* the local errno values which may not be the same.
|
||||
*/
|
||||
static const struct {
|
||||
int stat;
|
||||
int errno;
|
||||
} nfs4_errtbl[] = {
|
||||
{ NFS4_OK, 0 },
|
||||
{ NFS4ERR_PERM, -EPERM },
|
||||
{ NFS4ERR_NOENT, -ENOENT },
|
||||
{ NFS4ERR_IO, -errno_NFSERR_IO},
|
||||
{ NFS4ERR_NXIO, -ENXIO },
|
||||
{ NFS4ERR_ACCESS, -EACCES },
|
||||
{ NFS4ERR_EXIST, -EEXIST },
|
||||
{ NFS4ERR_XDEV, -EXDEV },
|
||||
{ NFS4ERR_NOTDIR, -ENOTDIR },
|
||||
{ NFS4ERR_ISDIR, -EISDIR },
|
||||
{ NFS4ERR_INVAL, -EINVAL },
|
||||
{ NFS4ERR_FBIG, -EFBIG },
|
||||
{ NFS4ERR_NOSPC, -ENOSPC },
|
||||
{ NFS4ERR_ROFS, -EROFS },
|
||||
{ NFS4ERR_MLINK, -EMLINK },
|
||||
{ NFS4ERR_NAMETOOLONG, -ENAMETOOLONG },
|
||||
{ NFS4ERR_NOTEMPTY, -ENOTEMPTY },
|
||||
{ NFS4ERR_DQUOT, -EDQUOT },
|
||||
{ NFS4ERR_STALE, -ESTALE },
|
||||
{ NFS4ERR_BADHANDLE, -EBADHANDLE },
|
||||
{ NFS4ERR_BAD_COOKIE, -EBADCOOKIE },
|
||||
{ NFS4ERR_NOTSUPP, -ENOTSUPP },
|
||||
{ NFS4ERR_TOOSMALL, -ETOOSMALL },
|
||||
{ NFS4ERR_SERVERFAULT, -EREMOTEIO },
|
||||
{ NFS4ERR_BADTYPE, -EBADTYPE },
|
||||
{ NFS4ERR_LOCKED, -EAGAIN },
|
||||
{ NFS4ERR_SYMLINK, -ELOOP },
|
||||
{ NFS4ERR_OP_ILLEGAL, -EOPNOTSUPP },
|
||||
{ NFS4ERR_DEADLOCK, -EDEADLK },
|
||||
{ NFS4ERR_NOXATTR, -ENODATA },
|
||||
{ NFS4ERR_XATTR2BIG, -E2BIG },
|
||||
{ -1, -EIO }
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert an NFS error code to a local one.
|
||||
* This one is used by NFSv4.
|
||||
*/
|
||||
int nfs4_stat_to_errno(int stat)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; nfs4_errtbl[i].stat != -1; i++) {
|
||||
if (nfs4_errtbl[i].stat == stat)
|
||||
return nfs4_errtbl[i].errno;
|
||||
}
|
||||
if (stat <= 10000 || stat > 10100) {
|
||||
/* The server is looney tunes. */
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
/* If we cannot translate the error, the recovery routines should
|
||||
* handle it.
|
||||
* Note: remaining NFSv4 error codes have values > 10000, so should
|
||||
* not conflict with native Linux error codes.
|
||||
*/
|
||||
return -stat;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs4_stat_to_errno);
|
172
fs/nfs_common/nfslocalio.c
Normal file
172
fs/nfs_common/nfslocalio.c
Normal file
@ -0,0 +1,172 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
|
||||
* Copyright (C) 2024 NeilBrown <neilb@suse.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/nfslocalio.h>
|
||||
#include <net/netns/generic.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("NFS localio protocol bypass support");
|
||||
|
||||
static DEFINE_SPINLOCK(nfs_uuid_lock);
|
||||
|
||||
/*
|
||||
* Global list of nfs_uuid_t instances
|
||||
* that is protected by nfs_uuid_lock.
|
||||
*/
|
||||
static LIST_HEAD(nfs_uuids);
|
||||
|
||||
void nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
|
||||
{
|
||||
nfs_uuid->net = NULL;
|
||||
nfs_uuid->dom = NULL;
|
||||
uuid_gen(&nfs_uuid->uuid);
|
||||
|
||||
spin_lock(&nfs_uuid_lock);
|
||||
list_add_tail_rcu(&nfs_uuid->list, &nfs_uuids);
|
||||
spin_unlock(&nfs_uuid_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_uuid_begin);
|
||||
|
||||
void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
|
||||
{
|
||||
if (nfs_uuid->net == NULL) {
|
||||
spin_lock(&nfs_uuid_lock);
|
||||
list_del_init(&nfs_uuid->list);
|
||||
spin_unlock(&nfs_uuid_lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_uuid_end);
|
||||
|
||||
static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
|
||||
{
|
||||
nfs_uuid_t *nfs_uuid;
|
||||
|
||||
list_for_each_entry(nfs_uuid, &nfs_uuids, list)
|
||||
if (uuid_equal(&nfs_uuid->uuid, uuid))
|
||||
return nfs_uuid;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct module *nfsd_mod;
|
||||
|
||||
void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
|
||||
struct net *net, struct auth_domain *dom,
|
||||
struct module *mod)
|
||||
{
|
||||
nfs_uuid_t *nfs_uuid;
|
||||
|
||||
spin_lock(&nfs_uuid_lock);
|
||||
nfs_uuid = nfs_uuid_lookup_locked(uuid);
|
||||
if (nfs_uuid) {
|
||||
kref_get(&dom->ref);
|
||||
nfs_uuid->dom = dom;
|
||||
/*
|
||||
* We don't hold a ref on the net, but instead put
|
||||
* ourselves on a list so the net pointer can be
|
||||
* invalidated.
|
||||
*/
|
||||
list_move(&nfs_uuid->list, list);
|
||||
rcu_assign_pointer(nfs_uuid->net, net);
|
||||
|
||||
__module_get(mod);
|
||||
nfsd_mod = mod;
|
||||
}
|
||||
spin_unlock(&nfs_uuid_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
|
||||
|
||||
static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
|
||||
{
|
||||
if (nfs_uuid->net) {
|
||||
module_put(nfsd_mod);
|
||||
nfs_uuid->net = NULL;
|
||||
}
|
||||
if (nfs_uuid->dom) {
|
||||
auth_domain_put(nfs_uuid->dom);
|
||||
nfs_uuid->dom = NULL;
|
||||
}
|
||||
list_del_init(&nfs_uuid->list);
|
||||
}
|
||||
|
||||
void nfs_uuid_invalidate_clients(struct list_head *list)
|
||||
{
|
||||
nfs_uuid_t *nfs_uuid, *tmp;
|
||||
|
||||
spin_lock(&nfs_uuid_lock);
|
||||
list_for_each_entry_safe(nfs_uuid, tmp, list, list)
|
||||
nfs_uuid_put_locked(nfs_uuid);
|
||||
spin_unlock(&nfs_uuid_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients);
|
||||
|
||||
void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid)
|
||||
{
|
||||
if (nfs_uuid->net) {
|
||||
spin_lock(&nfs_uuid_lock);
|
||||
nfs_uuid_put_locked(nfs_uuid);
|
||||
spin_unlock(&nfs_uuid_lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client);
|
||||
|
||||
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
|
||||
struct rpc_clnt *rpc_clnt, const struct cred *cred,
|
||||
const struct nfs_fh *nfs_fh, const fmode_t fmode)
|
||||
{
|
||||
struct net *net;
|
||||
struct nfsd_file *localio;
|
||||
|
||||
/*
|
||||
* Not running in nfsd context, so must safely get reference on nfsd_serv.
|
||||
* But the server may already be shutting down, if so disallow new localio.
|
||||
* uuid->net is NOT a counted reference, but rcu_read_lock() ensures that
|
||||
* if uuid->net is not NULL, then calling nfsd_serv_try_get() is safe
|
||||
* and if it succeeds we will have an implied reference to the net.
|
||||
*
|
||||
* Otherwise NFS may not have ref on NFSD and therefore cannot safely
|
||||
* make 'nfs_to' calls.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
net = rcu_dereference(uuid->net);
|
||||
if (!net || !nfs_to->nfsd_serv_try_get(net)) {
|
||||
rcu_read_unlock();
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
/* We have an implied reference to net thanks to nfsd_serv_try_get */
|
||||
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
|
||||
cred, nfs_fh, fmode);
|
||||
if (IS_ERR(localio))
|
||||
nfs_to->nfsd_serv_put(net);
|
||||
return localio;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_open_local_fh);
|
||||
|
||||
/*
|
||||
* The NFS LOCALIO code needs to call into NFSD using various symbols,
|
||||
* but cannot be statically linked, because that will make the NFS
|
||||
* module always depend on the NFSD module.
|
||||
*
|
||||
* 'nfs_to' provides NFS access to NFSD functions needed for LOCALIO,
|
||||
* its lifetime is tightly coupled to the NFSD module and will always
|
||||
* be available to NFS LOCALIO because any successful client<->server
|
||||
* LOCALIO handshake results in a reference on the NFSD module (above),
|
||||
* so NFS implicitly holds a reference to the NFSD module and its
|
||||
* functions in the 'nfs_to' nfsd_localio_operations cannot disappear.
|
||||
*
|
||||
* If the last NFS client using LOCALIO disconnects (and its reference
|
||||
* on NFSD dropped) then NFSD could be unloaded, resulting in 'nfs_to'
|
||||
* functions being invalid pointers. But if NFSD isn't loaded then NFS
|
||||
* will not be able to handshake with NFSD and will have no cause to
|
||||
* try to call 'nfs_to' function pointers. If/when NFSD is reloaded it
|
||||
* will reinitialize the 'nfs_to' function pointers and make LOCALIO
|
||||
* possible.
|
||||
*/
|
||||
const struct nfsd_localio_operations *nfs_to;
|
||||
EXPORT_SYMBOL_GPL(nfs_to);
|
@ -7,6 +7,7 @@ config NFSD
|
||||
select LOCKD
|
||||
select SUNRPC
|
||||
select EXPORTFS
|
||||
select NFS_COMMON
|
||||
select NFS_ACL_SUPPORT if NFSD_V2_ACL
|
||||
select NFS_ACL_SUPPORT if NFSD_V3_ACL
|
||||
depends on MULTIUSER
|
||||
|
@ -23,3 +23,4 @@ nfsd-$(CONFIG_NFSD_PNFS) += nfs4layouts.o
|
||||
nfsd-$(CONFIG_NFSD_BLOCKLAYOUT) += blocklayout.o blocklayoutxdr.o
|
||||
nfsd-$(CONFIG_NFSD_SCSILAYOUT) += blocklayout.o blocklayoutxdr.o
|
||||
nfsd-$(CONFIG_NFSD_FLEXFILELAYOUT) += flexfilelayout.o flexfilelayoutxdr.o
|
||||
nfsd-$(CONFIG_NFS_LOCALIO) += localio.o
|
||||
|
@ -1074,10 +1074,30 @@ static struct svc_export *exp_find(struct cache_detail *cd,
|
||||
return exp;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_nfsd_access - check if access to export is allowed.
|
||||
* @exp: svc_export that is being accessed.
|
||||
* @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO).
|
||||
*
|
||||
* Return values:
|
||||
* %nfs_ok if access is granted, or
|
||||
* %nfserr_wrongsec if access is denied
|
||||
*/
|
||||
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
|
||||
{
|
||||
struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
|
||||
struct svc_xprt *xprt = rqstp->rq_xprt;
|
||||
struct svc_xprt *xprt;
|
||||
|
||||
/*
|
||||
* If rqstp is NULL, this is a LOCALIO request which will only
|
||||
* ever use a filehandle/credential pair for which access has
|
||||
* been affirmed (by ACCESS or OPEN NFS requests) over the
|
||||
* wire. So there is no need for further checks here.
|
||||
*/
|
||||
if (!rqstp)
|
||||
return nfs_ok;
|
||||
|
||||
xprt = rqstp->rq_xprt;
|
||||
|
||||
if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_NONE) {
|
||||
if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags))
|
||||
@ -1098,17 +1118,17 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
|
||||
ok:
|
||||
/* legacy gss-only clients are always OK: */
|
||||
if (exp->ex_client == rqstp->rq_gssclient)
|
||||
return 0;
|
||||
return nfs_ok;
|
||||
/* ip-address based client; check sec= export option: */
|
||||
for (f = exp->ex_flavors; f < end; f++) {
|
||||
if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
|
||||
return 0;
|
||||
return nfs_ok;
|
||||
}
|
||||
/* defaults in absence of sec= options: */
|
||||
if (exp->ex_nflavors == 0) {
|
||||
if (rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL ||
|
||||
rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)
|
||||
return 0;
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
/* If the compound op contains a spo_must_allowed op,
|
||||
@ -1118,7 +1138,7 @@ ok:
|
||||
*/
|
||||
|
||||
if (nfsd4_spo_must_allow(rqstp))
|
||||
return 0;
|
||||
return nfs_ok;
|
||||
|
||||
denied:
|
||||
return nfserr_wrongsec;
|
||||
|
@ -52,7 +52,7 @@
|
||||
#define NFSD_FILE_CACHE_UP (0)
|
||||
|
||||
/* We only care about NFSD_MAY_READ/WRITE for this cache */
|
||||
#define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE)
|
||||
#define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
|
||||
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
|
||||
@ -390,6 +390,34 @@ nfsd_file_put(struct nfsd_file *nf)
|
||||
nfsd_file_free(nf);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_file_put_local - put the reference to nfsd_file and local nfsd_serv
|
||||
* @nf: nfsd_file of which to put the references
|
||||
*
|
||||
* First put the reference of the nfsd_file and then put the
|
||||
* reference to the associated nn->nfsd_serv.
|
||||
*/
|
||||
void
|
||||
nfsd_file_put_local(struct nfsd_file *nf)
|
||||
{
|
||||
struct net *net = nf->nf_net;
|
||||
|
||||
nfsd_file_put(nf);
|
||||
nfsd_serv_put(net);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_file_file - get the backing file of an nfsd_file
|
||||
* @nf: nfsd_file of which to access the backing file.
|
||||
*
|
||||
* Return backing file for @nf.
|
||||
*/
|
||||
struct file *
|
||||
nfsd_file_file(struct nfsd_file *nf)
|
||||
{
|
||||
return nf->nf_file;
|
||||
}
|
||||
|
||||
static void
|
||||
nfsd_file_dispose_list(struct list_head *dispose)
|
||||
{
|
||||
@ -982,12 +1010,14 @@ nfsd_file_is_cached(struct inode *inode)
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
|
||||
struct svc_cred *cred,
|
||||
struct auth_domain *client,
|
||||
struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct file *file,
|
||||
struct nfsd_file **pnf, bool want_gc)
|
||||
{
|
||||
unsigned char need = may_flags & NFSD_FILE_MAY_MASK;
|
||||
struct net *net = SVC_NET(rqstp);
|
||||
struct nfsd_file *new, *nf;
|
||||
bool stale_retry = true;
|
||||
bool open_retry = true;
|
||||
@ -996,8 +1026,13 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
status = fh_verify(rqstp, fhp, S_IFREG,
|
||||
may_flags|NFSD_MAY_OWNER_OVERRIDE);
|
||||
if (rqstp) {
|
||||
status = fh_verify(rqstp, fhp, S_IFREG,
|
||||
may_flags|NFSD_MAY_OWNER_OVERRIDE);
|
||||
} else {
|
||||
status = fh_verify_local(net, cred, client, fhp, S_IFREG,
|
||||
may_flags|NFSD_MAY_OWNER_OVERRIDE);
|
||||
}
|
||||
if (status != nfs_ok)
|
||||
return status;
|
||||
inode = d_inode(fhp->fh_dentry);
|
||||
@ -1143,7 +1178,8 @@ __be32
|
||||
nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct nfsd_file **pnf)
|
||||
{
|
||||
return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, true);
|
||||
return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
|
||||
fhp, may_flags, NULL, pnf, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1167,7 +1203,55 @@ __be32
|
||||
nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct nfsd_file **pnf)
|
||||
{
|
||||
return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, false);
|
||||
return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
|
||||
fhp, may_flags, NULL, pnf, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_file_acquire_local - Get a struct nfsd_file with an open file for localio
|
||||
* @net: The network namespace in which to perform a lookup
|
||||
* @cred: the user credential with which to validate access
|
||||
* @client: the auth_domain for LOCALIO lookup
|
||||
* @fhp: the NFS filehandle of the file to be opened
|
||||
* @may_flags: NFSD_MAY_ settings for the file
|
||||
* @pnf: OUT: new or found "struct nfsd_file" object
|
||||
*
|
||||
* This file lookup interface provide access to a file given the
|
||||
* filehandle and credential. No connection-based authorisation
|
||||
* is performed and in that way it is quite different to other
|
||||
* file access mediated by nfsd. It allows a kernel module such as the NFS
|
||||
* client to reach across network and filesystem namespaces to access
|
||||
* a file. The security implications of this should be carefully
|
||||
* considered before use.
|
||||
*
|
||||
* The nfsd_file object returned by this API is reference-counted
|
||||
* and garbage-collected. The object is retained for a few
|
||||
* seconds after the final nfsd_file_put() in case the caller
|
||||
* wants to re-use it.
|
||||
*
|
||||
* Return values:
|
||||
* %nfs_ok - @pnf points to an nfsd_file with its reference
|
||||
* count boosted.
|
||||
*
|
||||
* On error, an nfsstat value in network byte order is returned.
|
||||
*/
|
||||
__be32
|
||||
nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
|
||||
struct auth_domain *client, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct nfsd_file **pnf)
|
||||
{
|
||||
/*
|
||||
* Save creds before calling nfsd_file_do_acquire() (which calls
|
||||
* nfsd_setuser). Important because caller (LOCALIO) is from
|
||||
* client context.
|
||||
*/
|
||||
const struct cred *save_cred = get_current_cred();
|
||||
__be32 beres;
|
||||
|
||||
beres = nfsd_file_do_acquire(NULL, net, cred, client,
|
||||
fhp, may_flags, NULL, pnf, true);
|
||||
revert_creds(save_cred);
|
||||
return beres;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1193,7 +1277,8 @@ nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct file *file,
|
||||
struct nfsd_file **pnf)
|
||||
{
|
||||
return nfsd_file_do_acquire(rqstp, fhp, may_flags, file, pnf, false);
|
||||
return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
|
||||
fhp, may_flags, file, pnf, false);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -55,7 +55,9 @@ void nfsd_file_cache_shutdown(void);
|
||||
int nfsd_file_cache_start_net(struct net *net);
|
||||
void nfsd_file_cache_shutdown_net(struct net *net);
|
||||
void nfsd_file_put(struct nfsd_file *nf);
|
||||
void nfsd_file_put_local(struct nfsd_file *nf);
|
||||
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
|
||||
struct file *nfsd_file_file(struct nfsd_file *nf);
|
||||
void nfsd_file_close_inode_sync(struct inode *inode);
|
||||
void nfsd_file_net_dispose(struct nfsd_net *nn);
|
||||
bool nfsd_file_is_cached(struct inode *inode);
|
||||
@ -66,5 +68,8 @@ __be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
__be32 nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct file *file,
|
||||
struct nfsd_file **nfp);
|
||||
__be32 nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
|
||||
struct auth_domain *client, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct nfsd_file **pnf);
|
||||
int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
|
||||
#endif /* _FS_NFSD_FILECACHE_H */
|
||||
|
169
fs/nfsd/localio.c
Normal file
169
fs/nfsd/localio.c
Normal file
@ -0,0 +1,169 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NFS server support for local clients to bypass network stack
|
||||
*
|
||||
* Copyright (C) 2014 Weston Andros Adamson <dros@primarydata.com>
|
||||
* Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com>
|
||||
* Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
|
||||
* Copyright (C) 2024 NeilBrown <neilb@suse.de>
|
||||
*/
|
||||
|
||||
#include <linux/exportfs.h>
|
||||
#include <linux/sunrpc/svcauth.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs_common.h>
|
||||
#include <linux/nfslocalio.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_xdr.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "nfsd.h"
|
||||
#include "vfs.h"
|
||||
#include "netns.h"
|
||||
#include "filecache.h"
|
||||
#include "cache.h"
|
||||
|
||||
static const struct nfsd_localio_operations nfsd_localio_ops = {
|
||||
.nfsd_serv_try_get = nfsd_serv_try_get,
|
||||
.nfsd_serv_put = nfsd_serv_put,
|
||||
.nfsd_open_local_fh = nfsd_open_local_fh,
|
||||
.nfsd_file_put_local = nfsd_file_put_local,
|
||||
.nfsd_file_file = nfsd_file_file,
|
||||
};
|
||||
|
||||
void nfsd_localio_ops_init(void)
|
||||
{
|
||||
nfs_to = &nfsd_localio_ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_open_local_fh - lookup a local filehandle @nfs_fh and map to nfsd_file
|
||||
*
|
||||
* @net: 'struct net' to get the proper nfsd_net required for LOCALIO access
|
||||
* @dom: 'struct auth_domain' required for LOCALIO access
|
||||
* @rpc_clnt: rpc_clnt that the client established
|
||||
* @cred: cred that the client established
|
||||
* @nfs_fh: filehandle to lookup
|
||||
* @fmode: fmode_t to use for open
|
||||
*
|
||||
* This function maps a local fh to a path on a local filesystem.
|
||||
* This is useful when the nfs client has the local server mounted - it can
|
||||
* avoid all the NFS overhead with reads, writes and commits.
|
||||
*
|
||||
* On successful return, returned nfsd_file will have its nf_net member
|
||||
* set. Caller (NFS client) is responsible for calling nfsd_serv_put and
|
||||
* nfsd_file_put (via nfs_to->nfsd_file_put_local).
|
||||
*/
|
||||
struct nfsd_file *
|
||||
nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
|
||||
struct rpc_clnt *rpc_clnt, const struct cred *cred,
|
||||
const struct nfs_fh *nfs_fh, const fmode_t fmode)
|
||||
{
|
||||
int mayflags = NFSD_MAY_LOCALIO;
|
||||
struct svc_cred rq_cred;
|
||||
struct svc_fh fh;
|
||||
struct nfsd_file *localio;
|
||||
__be32 beres;
|
||||
|
||||
if (nfs_fh->size > NFS4_FHSIZE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* nfs_fh -> svc_fh */
|
||||
fh_init(&fh, NFS4_FHSIZE);
|
||||
fh.fh_handle.fh_size = nfs_fh->size;
|
||||
memcpy(fh.fh_handle.fh_raw, nfs_fh->data, nfs_fh->size);
|
||||
|
||||
if (fmode & FMODE_READ)
|
||||
mayflags |= NFSD_MAY_READ;
|
||||
if (fmode & FMODE_WRITE)
|
||||
mayflags |= NFSD_MAY_WRITE;
|
||||
|
||||
svcauth_map_clnt_to_svc_cred_local(rpc_clnt, cred, &rq_cred);
|
||||
|
||||
beres = nfsd_file_acquire_local(net, &rq_cred, dom,
|
||||
&fh, mayflags, &localio);
|
||||
if (beres)
|
||||
localio = ERR_PTR(nfs_stat_to_errno(be32_to_cpu(beres)));
|
||||
|
||||
fh_put(&fh);
|
||||
if (rq_cred.cr_group_info)
|
||||
put_group_info(rq_cred.cr_group_info);
|
||||
|
||||
return localio;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfsd_open_local_fh);
|
||||
|
||||
/*
|
||||
* UUID_IS_LOCAL XDR functions
|
||||
*/
|
||||
|
||||
static __be32 localio_proc_null(struct svc_rqst *rqstp)
|
||||
{
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
struct localio_uuidarg {
|
||||
uuid_t uuid;
|
||||
};
|
||||
|
||||
static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct localio_uuidarg *argp = rqstp->rq_argp;
|
||||
struct net *net = SVC_NET(rqstp);
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
|
||||
nfs_uuid_is_local(&argp->uuid, &nn->local_clients,
|
||||
net, rqstp->rq_client, THIS_MODULE);
|
||||
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static bool localio_decode_uuidarg(struct svc_rqst *rqstp,
|
||||
struct xdr_stream *xdr)
|
||||
{
|
||||
struct localio_uuidarg *argp = rqstp->rq_argp;
|
||||
u8 uuid[UUID_SIZE];
|
||||
|
||||
if (decode_opaque_fixed(xdr, uuid, UUID_SIZE))
|
||||
return false;
|
||||
import_uuid(&argp->uuid, uuid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct svc_procedure localio_procedures1[] = {
|
||||
[LOCALIOPROC_NULL] = {
|
||||
.pc_func = localio_proc_null,
|
||||
.pc_decode = nfssvc_decode_voidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[LOCALIOPROC_UUID_IS_LOCAL] = {
|
||||
.pc_func = localio_proc_uuid_is_local,
|
||||
.pc_decode = localio_decode_uuidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct localio_uuidarg),
|
||||
.pc_argzero = sizeof(struct localio_uuidarg),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_name = "UUID_IS_LOCAL",
|
||||
},
|
||||
};
|
||||
|
||||
#define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1)
|
||||
static DEFINE_PER_CPU_ALIGNED(unsigned long,
|
||||
localio_count[LOCALIO_NR_PROCEDURES]);
|
||||
const struct svc_version localio_version1 = {
|
||||
.vs_vers = 1,
|
||||
.vs_nproc = LOCALIO_NR_PROCEDURES,
|
||||
.vs_proc = localio_procedures1,
|
||||
.vs_dispatch = nfsd_dispatch,
|
||||
.vs_count = localio_count,
|
||||
.vs_xdrsize = XDR_QUADLEN(UUID_SIZE),
|
||||
.vs_hidden = true,
|
||||
};
|
@ -13,6 +13,7 @@
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/nfs4.h>
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <linux/percpu-refcount.h>
|
||||
#include <linux/siphash.h>
|
||||
#include <linux/sunrpc/stats.h>
|
||||
|
||||
@ -139,7 +140,9 @@ struct nfsd_net {
|
||||
|
||||
struct svc_info nfsd_info;
|
||||
#define nfsd_serv nfsd_info.serv
|
||||
|
||||
struct percpu_ref nfsd_serv_ref;
|
||||
struct completion nfsd_serv_confirm_done;
|
||||
struct completion nfsd_serv_free_done;
|
||||
|
||||
/*
|
||||
* clientid and stateid data for construction of net unique COPY
|
||||
@ -214,6 +217,10 @@ struct nfsd_net {
|
||||
/* last time an admin-revoke happened for NFSv4.0 */
|
||||
time64_t nfs40_last_revoke;
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
/* Local clients to be invalidated when net is shut down */
|
||||
struct list_head local_clients;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Simple check to find out if a given net was properly initialized */
|
||||
@ -222,6 +229,9 @@ struct nfsd_net {
|
||||
extern bool nfsd_support_version(int vers);
|
||||
extern unsigned int nfsd_net_id;
|
||||
|
||||
bool nfsd_serv_try_get(struct net *net);
|
||||
void nfsd_serv_put(struct net *net);
|
||||
|
||||
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn);
|
||||
void nfsd_reset_write_verifier(struct nfsd_net *nn);
|
||||
#endif /* __NFSD_NETNS_H__ */
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/nfslocalio.h>
|
||||
|
||||
#include "idmap.h"
|
||||
#include "nfsd.h"
|
||||
@ -2246,7 +2247,7 @@ static __net_init int nfsd_net_init(struct net *net)
|
||||
if (retval)
|
||||
goto out_repcache_error;
|
||||
memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats));
|
||||
nn->nfsd_svcstats.program = &nfsd_program;
|
||||
nn->nfsd_svcstats.program = &nfsd_programs[0];
|
||||
for (i = 0; i < sizeof(nn->nfsd_versions); i++)
|
||||
nn->nfsd_versions[i] = nfsd_support_version(i);
|
||||
for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++)
|
||||
@ -2257,7 +2258,9 @@ static __net_init int nfsd_net_init(struct net *net)
|
||||
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
|
||||
seqlock_init(&nn->writeverf_lock);
|
||||
nfsd_proc_stat_init(net);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
INIT_LIST_HEAD(&nn->local_clients);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
out_repcache_error:
|
||||
@ -2268,6 +2271,22 @@ out_export_error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
/**
|
||||
* nfsd_net_pre_exit - Disconnect localio clients from net namespace
|
||||
* @net: a network namespace that is about to be destroyed
|
||||
*
|
||||
* This invalidated ->net pointers held by localio clients
|
||||
* while they can still safely access nn->counter.
|
||||
*/
|
||||
static __net_exit void nfsd_net_pre_exit(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
|
||||
nfs_uuid_invalidate_clients(&nn->local_clients);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* nfsd_net_exit - Release the nfsd_net portion of a net namespace
|
||||
* @net: a network namespace that is about to be destroyed
|
||||
@ -2285,6 +2304,9 @@ static __net_exit void nfsd_net_exit(struct net *net)
|
||||
|
||||
static struct pernet_operations nfsd_net_ops = {
|
||||
.init = nfsd_net_init,
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
.pre_exit = nfsd_net_pre_exit,
|
||||
#endif
|
||||
.exit = nfsd_net_exit,
|
||||
.id = &nfsd_net_id,
|
||||
.size = sizeof(struct nfsd_net),
|
||||
@ -2322,6 +2344,7 @@ static int __init init_nfsd(void)
|
||||
retval = genl_register_family(&nfsd_nl_family);
|
||||
if (retval)
|
||||
goto out_free_all;
|
||||
nfsd_localio_ops_init();
|
||||
|
||||
return 0;
|
||||
out_free_all:
|
||||
|
@ -85,7 +85,7 @@ struct nfsd_genl_rqstp {
|
||||
u32 rq_opnum[NFSD_MAX_OPS_PER_COMPOUND];
|
||||
};
|
||||
|
||||
extern struct svc_program nfsd_program;
|
||||
extern struct svc_program nfsd_programs[];
|
||||
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
|
||||
extern struct mutex nfsd_mutex;
|
||||
extern spinlock_t nfsd_drc_lock;
|
||||
@ -146,6 +146,10 @@ extern const struct svc_version nfsd_acl_version3;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
extern const struct svc_version localio_version1;
|
||||
#endif
|
||||
|
||||
struct nfsd_net;
|
||||
|
||||
enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
|
||||
|
227
fs/nfsd/nfsfh.c
227
fs/nfsd/nfsfh.c
@ -87,23 +87,24 @@ nfsd_mode_check(struct dentry *dentry, umode_t requested)
|
||||
return nfserr_wrong_type;
|
||||
}
|
||||
|
||||
static bool nfsd_originating_port_ok(struct svc_rqst *rqstp, int flags)
|
||||
static bool nfsd_originating_port_ok(struct svc_rqst *rqstp,
|
||||
struct svc_cred *cred,
|
||||
struct svc_export *exp)
|
||||
{
|
||||
if (flags & NFSEXP_INSECURE_PORT)
|
||||
if (nfsexp_flags(cred, exp) & NFSEXP_INSECURE_PORT)
|
||||
return true;
|
||||
/* We don't require gss requests to use low ports: */
|
||||
if (rqstp->rq_cred.cr_flavor >= RPC_AUTH_GSS)
|
||||
if (cred->cr_flavor >= RPC_AUTH_GSS)
|
||||
return true;
|
||||
return test_bit(RQ_SECURE, &rqstp->rq_flags);
|
||||
}
|
||||
|
||||
static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
|
||||
struct svc_cred *cred,
|
||||
struct svc_export *exp)
|
||||
{
|
||||
int flags = nfsexp_flags(&rqstp->rq_cred, exp);
|
||||
|
||||
/* Check if the request originated from a secure port. */
|
||||
if (!nfsd_originating_port_ok(rqstp, flags)) {
|
||||
if (rqstp && !nfsd_originating_port_ok(rqstp, cred, exp)) {
|
||||
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
|
||||
dprintk("nfsd: request from insecure port %s!\n",
|
||||
svc_print_addr(rqstp, buf, sizeof(buf)));
|
||||
@ -111,7 +112,7 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
|
||||
}
|
||||
|
||||
/* Set user creds for this exportpoint */
|
||||
return nfserrno(nfsd_setuser(&rqstp->rq_cred, exp));
|
||||
return nfserrno(nfsd_setuser(cred, exp));
|
||||
}
|
||||
|
||||
static inline __be32 check_pseudo_root(struct dentry *dentry,
|
||||
@ -141,7 +142,11 @@ static inline __be32 check_pseudo_root(struct dentry *dentry,
|
||||
* dentry. On success, the results are used to set fh_export and
|
||||
* fh_dentry.
|
||||
*/
|
||||
static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net,
|
||||
struct svc_cred *cred,
|
||||
struct auth_domain *client,
|
||||
struct auth_domain *gssclient,
|
||||
struct svc_fh *fhp)
|
||||
{
|
||||
struct knfsd_fh *fh = &fhp->fh_handle;
|
||||
struct fid *fid = NULL;
|
||||
@ -183,8 +188,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
data_left -= len;
|
||||
if (data_left < 0)
|
||||
return error;
|
||||
exp = rqst_exp_find(&rqstp->rq_chandle, SVC_NET(rqstp),
|
||||
rqstp->rq_client, rqstp->rq_gssclient,
|
||||
exp = rqst_exp_find(rqstp ? &rqstp->rq_chandle : NULL,
|
||||
net, client, gssclient,
|
||||
fh->fh_fsid_type, fh->fh_fsid);
|
||||
fid = (struct fid *)(fh->fh_fsid + len);
|
||||
|
||||
@ -219,7 +224,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
put_cred(override_creds(new));
|
||||
put_cred(new);
|
||||
} else {
|
||||
error = nfsd_setuser_and_check_port(rqstp, exp);
|
||||
error = nfsd_setuser_and_check_port(rqstp, cred, exp);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
@ -266,20 +271,20 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
fhp->fh_dentry = dentry;
|
||||
fhp->fh_export = exp;
|
||||
|
||||
switch (rqstp->rq_vers) {
|
||||
case 4:
|
||||
switch (fhp->fh_maxsize) {
|
||||
case NFS4_FHSIZE:
|
||||
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
|
||||
fhp->fh_no_atomic_attr = true;
|
||||
fhp->fh_64bit_cookies = true;
|
||||
break;
|
||||
case 3:
|
||||
case NFS3_FHSIZE:
|
||||
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC)
|
||||
fhp->fh_no_wcc = true;
|
||||
fhp->fh_64bit_cookies = true;
|
||||
if (exp->ex_flags & NFSEXP_V4ROOT)
|
||||
goto out;
|
||||
break;
|
||||
case 2:
|
||||
case NFS_FHSIZE:
|
||||
fhp->fh_no_wcc = true;
|
||||
if (EX_WGATHER(exp))
|
||||
fhp->fh_use_wgather = true;
|
||||
@ -293,6 +298,123 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* __fh_verify - filehandle lookup and access checking
|
||||
* @rqstp: RPC transaction context, or NULL
|
||||
* @net: net namespace in which to perform the export lookup
|
||||
* @cred: RPC user credential
|
||||
* @client: RPC auth domain
|
||||
* @gssclient: RPC GSS auth domain, or NULL
|
||||
* @fhp: filehandle to be verified
|
||||
* @type: expected type of object pointed to by filehandle
|
||||
* @access: type of access needed to object
|
||||
*
|
||||
* See fh_verify() for further descriptions of @fhp, @type, and @access.
|
||||
*/
|
||||
static __be32
|
||||
__fh_verify(struct svc_rqst *rqstp,
|
||||
struct net *net, struct svc_cred *cred,
|
||||
struct auth_domain *client,
|
||||
struct auth_domain *gssclient,
|
||||
struct svc_fh *fhp, umode_t type, int access)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_export *exp = NULL;
|
||||
struct dentry *dentry;
|
||||
__be32 error;
|
||||
|
||||
if (!fhp->fh_dentry) {
|
||||
error = nfsd_set_fh_dentry(rqstp, net, cred, client,
|
||||
gssclient, fhp);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
dentry = fhp->fh_dentry;
|
||||
exp = fhp->fh_export;
|
||||
|
||||
trace_nfsd_fh_verify(rqstp, fhp, type, access);
|
||||
|
||||
/*
|
||||
* We still have to do all these permission checks, even when
|
||||
* fh_dentry is already set:
|
||||
* - fh_verify may be called multiple times with different
|
||||
* "access" arguments (e.g. nfsd_proc_create calls
|
||||
* fh_verify(...,NFSD_MAY_EXEC) first, then later (in
|
||||
* nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE).
|
||||
* - in the NFSv4 case, the filehandle may have been filled
|
||||
* in by fh_compose, and given a dentry, but further
|
||||
* compound operations performed with that filehandle
|
||||
* still need permissions checks. In the worst case, a
|
||||
* mountpoint crossing may have changed the export
|
||||
* options, and we may now need to use a different uid
|
||||
* (for example, if different id-squashing options are in
|
||||
* effect on the new filesystem).
|
||||
*/
|
||||
error = check_pseudo_root(dentry, exp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = nfsd_setuser_and_check_port(rqstp, cred, exp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = nfsd_mode_check(dentry, type);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* pseudoflavor restrictions are not enforced on NLM,
|
||||
* which clients virtually always use auth_sys for,
|
||||
* even while using RPCSEC_GSS for NFS.
|
||||
*/
|
||||
if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
|
||||
goto skip_pseudoflavor_check;
|
||||
/*
|
||||
* Clients may expect to be able to use auth_sys during mount,
|
||||
* even if they use gss for everything else; see section 2.3.2
|
||||
* of rfc 2623.
|
||||
*/
|
||||
if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
|
||||
&& exp->ex_path.dentry == dentry)
|
||||
goto skip_pseudoflavor_check;
|
||||
|
||||
error = check_nfsd_access(exp, rqstp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
skip_pseudoflavor_check:
|
||||
/* Finally, check access permissions. */
|
||||
error = nfsd_permission(cred, exp, dentry, access);
|
||||
out:
|
||||
trace_nfsd_fh_verify_err(rqstp, fhp, type, access, error);
|
||||
if (error == nfserr_stale)
|
||||
nfsd_stats_fh_stale_inc(nn, exp);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* fh_verify_local - filehandle lookup and access checking
|
||||
* @net: net namespace in which to perform the export lookup
|
||||
* @cred: RPC user credential
|
||||
* @client: RPC auth domain
|
||||
* @fhp: filehandle to be verified
|
||||
* @type: expected type of object pointed to by filehandle
|
||||
* @access: type of access needed to object
|
||||
*
|
||||
* This API can be used by callers who do not have an RPC
|
||||
* transaction context (ie are not running in an nfsd thread).
|
||||
*
|
||||
* See fh_verify() for further descriptions of @fhp, @type, and @access.
|
||||
*/
|
||||
__be32
|
||||
fh_verify_local(struct net *net, struct svc_cred *cred,
|
||||
struct auth_domain *client, struct svc_fh *fhp,
|
||||
umode_t type, int access)
|
||||
{
|
||||
return __fh_verify(NULL, net, cred, client, NULL,
|
||||
fhp, type, access);
|
||||
}
|
||||
|
||||
/**
|
||||
* fh_verify - filehandle lookup and access checking
|
||||
* @rqstp: pointer to current rpc request
|
||||
@ -323,80 +445,11 @@ out:
|
||||
__be32
|
||||
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
struct svc_export *exp = NULL;
|
||||
struct dentry *dentry;
|
||||
__be32 error;
|
||||
|
||||
if (!fhp->fh_dentry) {
|
||||
error = nfsd_set_fh_dentry(rqstp, fhp);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
dentry = fhp->fh_dentry;
|
||||
exp = fhp->fh_export;
|
||||
|
||||
trace_nfsd_fh_verify(rqstp, fhp, type, access);
|
||||
|
||||
/*
|
||||
* We still have to do all these permission checks, even when
|
||||
* fh_dentry is already set:
|
||||
* - fh_verify may be called multiple times with different
|
||||
* "access" arguments (e.g. nfsd_proc_create calls
|
||||
* fh_verify(...,NFSD_MAY_EXEC) first, then later (in
|
||||
* nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE).
|
||||
* - in the NFSv4 case, the filehandle may have been filled
|
||||
* in by fh_compose, and given a dentry, but further
|
||||
* compound operations performed with that filehandle
|
||||
* still need permissions checks. In the worst case, a
|
||||
* mountpoint crossing may have changed the export
|
||||
* options, and we may now need to use a different uid
|
||||
* (for example, if different id-squashing options are in
|
||||
* effect on the new filesystem).
|
||||
*/
|
||||
error = check_pseudo_root(dentry, exp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = nfsd_setuser_and_check_port(rqstp, exp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = nfsd_mode_check(dentry, type);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* pseudoflavor restrictions are not enforced on NLM,
|
||||
* which clients virtually always use auth_sys for,
|
||||
* even while using RPCSEC_GSS for NFS.
|
||||
*/
|
||||
if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
|
||||
goto skip_pseudoflavor_check;
|
||||
/*
|
||||
* Clients may expect to be able to use auth_sys during mount,
|
||||
* even if they use gss for everything else; see section 2.3.2
|
||||
* of rfc 2623.
|
||||
*/
|
||||
if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
|
||||
&& exp->ex_path.dentry == dentry)
|
||||
goto skip_pseudoflavor_check;
|
||||
|
||||
error = check_nfsd_access(exp, rqstp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
skip_pseudoflavor_check:
|
||||
/* Finally, check access permissions. */
|
||||
error = nfsd_permission(&rqstp->rq_cred, exp, dentry, access);
|
||||
out:
|
||||
trace_nfsd_fh_verify_err(rqstp, fhp, type, access, error);
|
||||
if (error == nfserr_stale)
|
||||
nfsd_stats_fh_stale_inc(nn, exp);
|
||||
return error;
|
||||
return __fh_verify(rqstp, SVC_NET(rqstp), &rqstp->rq_cred,
|
||||
rqstp->rq_client, rqstp->rq_gssclient,
|
||||
fhp, type, access);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compose a file handle for an NFS reply.
|
||||
*
|
||||
|
@ -217,6 +217,8 @@ extern char * SVCFH_fmt(struct svc_fh *fhp);
|
||||
* Function prototypes
|
||||
*/
|
||||
__be32 fh_verify(struct svc_rqst *, struct svc_fh *, umode_t, int);
|
||||
__be32 fh_verify_local(struct net *, struct svc_cred *, struct auth_domain *,
|
||||
struct svc_fh *, umode_t, int);
|
||||
__be32 fh_compose(struct svc_fh *, struct svc_export *, struct dentry *, struct svc_fh *);
|
||||
__be32 fh_update(struct svc_fh *);
|
||||
void fh_put(struct svc_fh *);
|
||||
|
105
fs/nfsd/nfssvc.c
105
fs/nfsd/nfssvc.c
@ -19,6 +19,7 @@
|
||||
#include <linux/sunrpc/svc_xprt.h>
|
||||
#include <linux/lockd/bind.h>
|
||||
#include <linux/nfsacl.h>
|
||||
#include <linux/nfslocalio.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <net/addrconf.h>
|
||||
@ -35,7 +36,6 @@
|
||||
#define NFSDDBG_FACILITY NFSDDBG_SVC
|
||||
|
||||
atomic_t nfsd_th_cnt = ATOMIC_INIT(0);
|
||||
extern struct svc_program nfsd_program;
|
||||
static int nfsd(void *vrqstp);
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
static int nfsd_acl_rpcbind_set(struct net *,
|
||||
@ -80,6 +80,15 @@ DEFINE_SPINLOCK(nfsd_drc_lock);
|
||||
unsigned long nfsd_drc_max_mem;
|
||||
unsigned long nfsd_drc_mem_used;
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
static const struct svc_version *localio_versions[] = {
|
||||
[1] = &localio_version1,
|
||||
};
|
||||
|
||||
#define NFSD_LOCALIO_NRVERS ARRAY_SIZE(localio_versions)
|
||||
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
static const struct svc_version *nfsd_acl_version[] = {
|
||||
# if defined(CONFIG_NFSD_V2_ACL)
|
||||
@ -90,20 +99,9 @@ static const struct svc_version *nfsd_acl_version[] = {
|
||||
# endif
|
||||
};
|
||||
|
||||
#define NFSD_ACL_MINVERS 2
|
||||
#define NFSD_ACL_MINVERS 2
|
||||
#define NFSD_ACL_NRVERS ARRAY_SIZE(nfsd_acl_version)
|
||||
|
||||
static struct svc_program nfsd_acl_program = {
|
||||
.pg_prog = NFS_ACL_PROGRAM,
|
||||
.pg_nvers = NFSD_ACL_NRVERS,
|
||||
.pg_vers = nfsd_acl_version,
|
||||
.pg_name = "nfsacl",
|
||||
.pg_class = "nfsd",
|
||||
.pg_authenticate = &svc_set_client,
|
||||
.pg_init_request = nfsd_acl_init_request,
|
||||
.pg_rpcbind_set = nfsd_acl_rpcbind_set,
|
||||
};
|
||||
|
||||
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
|
||||
|
||||
static const struct svc_version *nfsd_version[NFSD_MAXVERS+1] = {
|
||||
@ -116,18 +114,41 @@ static const struct svc_version *nfsd_version[NFSD_MAXVERS+1] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct svc_program nfsd_program = {
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
.pg_next = &nfsd_acl_program,
|
||||
#endif
|
||||
struct svc_program nfsd_programs[] = {
|
||||
{
|
||||
.pg_prog = NFS_PROGRAM, /* program number */
|
||||
.pg_nvers = NFSD_MAXVERS+1, /* nr of entries in nfsd_version */
|
||||
.pg_vers = nfsd_version, /* version table */
|
||||
.pg_name = "nfsd", /* program name */
|
||||
.pg_class = "nfsd", /* authentication class */
|
||||
.pg_authenticate = &svc_set_client, /* export authentication */
|
||||
.pg_authenticate = svc_set_client, /* export authentication */
|
||||
.pg_init_request = nfsd_init_request,
|
||||
.pg_rpcbind_set = nfsd_rpcbind_set,
|
||||
},
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
{
|
||||
.pg_prog = NFS_ACL_PROGRAM,
|
||||
.pg_nvers = NFSD_ACL_NRVERS,
|
||||
.pg_vers = nfsd_acl_version,
|
||||
.pg_name = "nfsacl",
|
||||
.pg_class = "nfsd",
|
||||
.pg_authenticate = svc_set_client,
|
||||
.pg_init_request = nfsd_acl_init_request,
|
||||
.pg_rpcbind_set = nfsd_acl_rpcbind_set,
|
||||
},
|
||||
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
{
|
||||
.pg_prog = NFS_LOCALIO_PROGRAM,
|
||||
.pg_nvers = NFSD_LOCALIO_NRVERS,
|
||||
.pg_vers = localio_versions,
|
||||
.pg_name = "nfslocalio",
|
||||
.pg_class = "nfsd",
|
||||
.pg_authenticate = svc_set_client,
|
||||
.pg_init_request = svc_generic_init_request,
|
||||
.pg_rpcbind_set = svc_generic_rpcbind_set,
|
||||
}
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
};
|
||||
|
||||
bool nfsd_support_version(int vers)
|
||||
@ -193,6 +214,34 @@ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool nfsd_serv_try_get(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
|
||||
return (nn && percpu_ref_tryget_live(&nn->nfsd_serv_ref));
|
||||
}
|
||||
|
||||
void nfsd_serv_put(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
|
||||
percpu_ref_put(&nn->nfsd_serv_ref);
|
||||
}
|
||||
|
||||
static void nfsd_serv_done(struct percpu_ref *ref)
|
||||
{
|
||||
struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_serv_ref);
|
||||
|
||||
complete(&nn->nfsd_serv_confirm_done);
|
||||
}
|
||||
|
||||
static void nfsd_serv_free(struct percpu_ref *ref)
|
||||
{
|
||||
struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_serv_ref);
|
||||
|
||||
complete(&nn->nfsd_serv_free_done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Maximum number of nfsd processes
|
||||
*/
|
||||
@ -392,6 +441,7 @@ static void nfsd_shutdown_net(struct net *net)
|
||||
lockd_down(net);
|
||||
nn->lockd_up = false;
|
||||
}
|
||||
percpu_ref_exit(&nn->nfsd_serv_ref);
|
||||
nn->nfsd_net_up = false;
|
||||
nfsd_shutdown_generic();
|
||||
}
|
||||
@ -471,6 +521,13 @@ void nfsd_destroy_serv(struct net *net)
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv = nn->nfsd_serv;
|
||||
|
||||
lockdep_assert_held(&nfsd_mutex);
|
||||
|
||||
percpu_ref_kill_and_confirm(&nn->nfsd_serv_ref, nfsd_serv_done);
|
||||
wait_for_completion(&nn->nfsd_serv_confirm_done);
|
||||
wait_for_completion(&nn->nfsd_serv_free_done);
|
||||
/* percpu_ref_exit is called in nfsd_shutdown_net */
|
||||
|
||||
spin_lock(&nfsd_notifier_lock);
|
||||
nn->nfsd_serv = NULL;
|
||||
spin_unlock(&nfsd_notifier_lock);
|
||||
@ -595,10 +652,18 @@ int nfsd_create_serv(struct net *net)
|
||||
if (nn->nfsd_serv)
|
||||
return 0;
|
||||
|
||||
error = percpu_ref_init(&nn->nfsd_serv_ref, nfsd_serv_free,
|
||||
0, GFP_KERNEL);
|
||||
if (error)
|
||||
return error;
|
||||
init_completion(&nn->nfsd_serv_free_done);
|
||||
init_completion(&nn->nfsd_serv_confirm_done);
|
||||
|
||||
if (nfsd_max_blksize == 0)
|
||||
nfsd_max_blksize = nfsd_get_default_max_blksize();
|
||||
nfsd_reset_versions(nn);
|
||||
serv = svc_create_pooled(&nfsd_program, &nn->nfsd_svcstats,
|
||||
serv = svc_create_pooled(nfsd_programs, ARRAY_SIZE(nfsd_programs),
|
||||
&nn->nfsd_svcstats,
|
||||
nfsd_max_blksize, nfsd);
|
||||
if (serv == NULL)
|
||||
return -ENOMEM;
|
||||
@ -905,7 +970,7 @@ nfsd(void *vrqstp)
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_dispatch - Process an NFS or NFSACL Request
|
||||
* nfsd_dispatch - Process an NFS or NFSACL or LOCALIO Request
|
||||
* @rqstp: incoming request
|
||||
*
|
||||
* This RPC dispatcher integrates the NFS server's duplicate reply cache.
|
||||
|
@ -86,7 +86,8 @@ DEFINE_NFSD_XDR_ERR_EVENT(cant_encode);
|
||||
{ NFSD_MAY_NOT_BREAK_LEASE, "NOT_BREAK_LEASE" }, \
|
||||
{ NFSD_MAY_BYPASS_GSS, "BYPASS_GSS" }, \
|
||||
{ NFSD_MAY_READ_IF_EXEC, "READ_IF_EXEC" }, \
|
||||
{ NFSD_MAY_64BIT_COOKIE, "64BIT_COOKIE" })
|
||||
{ NFSD_MAY_64BIT_COOKIE, "64BIT_COOKIE" }, \
|
||||
{ NFSD_MAY_LOCALIO, "LOCALIO" })
|
||||
|
||||
TRACE_EVENT(nfsd_compound,
|
||||
TP_PROTO(
|
||||
@ -193,7 +194,7 @@ TRACE_EVENT(nfsd_compound_encode_err,
|
||||
{ S_IFIFO, "FIFO" }, \
|
||||
{ S_IFSOCK, "SOCK" })
|
||||
|
||||
TRACE_EVENT(nfsd_fh_verify,
|
||||
TRACE_EVENT_CONDITION(nfsd_fh_verify,
|
||||
TP_PROTO(
|
||||
const struct svc_rqst *rqstp,
|
||||
const struct svc_fh *fhp,
|
||||
@ -201,6 +202,7 @@ TRACE_EVENT(nfsd_fh_verify,
|
||||
int access
|
||||
),
|
||||
TP_ARGS(rqstp, fhp, type, access),
|
||||
TP_CONDITION(rqstp != NULL),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, netns_ino)
|
||||
__sockaddr(server, rqstp->rq_xprt->xpt_remotelen)
|
||||
@ -239,7 +241,7 @@ TRACE_EVENT_CONDITION(nfsd_fh_verify_err,
|
||||
__be32 error
|
||||
),
|
||||
TP_ARGS(rqstp, fhp, type, access, error),
|
||||
TP_CONDITION(error),
|
||||
TP_CONDITION(rqstp != NULL && error),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, netns_ino)
|
||||
__sockaddr(server, rqstp->rq_xprt->xpt_remotelen)
|
||||
@ -295,12 +297,13 @@ DECLARE_EVENT_CLASS(nfsd_fh_err_class,
|
||||
__entry->status)
|
||||
)
|
||||
|
||||
#define DEFINE_NFSD_FH_ERR_EVENT(name) \
|
||||
DEFINE_EVENT(nfsd_fh_err_class, nfsd_##name, \
|
||||
TP_PROTO(struct svc_rqst *rqstp, \
|
||||
struct svc_fh *fhp, \
|
||||
int status), \
|
||||
TP_ARGS(rqstp, fhp, status))
|
||||
#define DEFINE_NFSD_FH_ERR_EVENT(name) \
|
||||
DEFINE_EVENT_CONDITION(nfsd_fh_err_class, nfsd_##name, \
|
||||
TP_PROTO(struct svc_rqst *rqstp, \
|
||||
struct svc_fh *fhp, \
|
||||
int status), \
|
||||
TP_ARGS(rqstp, fhp, status), \
|
||||
TP_CONDITION(rqstp != NULL))
|
||||
|
||||
DEFINE_NFSD_FH_ERR_EVENT(set_fh_dentry_badexport);
|
||||
DEFINE_NFSD_FH_ERR_EVENT(set_fh_dentry_badhandle);
|
||||
|
@ -33,6 +33,8 @@
|
||||
|
||||
#define NFSD_MAY_64BIT_COOKIE 0x1000 /* 64 bit readdir cookies for >= NFSv3 */
|
||||
|
||||
#define NFSD_MAY_LOCALIO 0x2000 /* for tracing, reflects when localio used */
|
||||
|
||||
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
|
||||
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
|
||||
|
||||
|
@ -8,11 +8,20 @@
|
||||
#ifndef _LINUX_NFS_H
|
||||
#define _LINUX_NFS_H
|
||||
|
||||
#include <linux/cred.h>
|
||||
#include <linux/sunrpc/auth.h>
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <uapi/linux/nfs.h>
|
||||
|
||||
/* The LOCALIO program is entirely private to Linux and is
|
||||
* NOT part of the uapi.
|
||||
*/
|
||||
#define NFS_LOCALIO_PROGRAM 400122
|
||||
#define LOCALIOPROC_NULL 0
|
||||
#define LOCALIOPROC_UUID_IS_LOCAL 1
|
||||
|
||||
/*
|
||||
* This is the kernel NFS client file handle representation
|
||||
*/
|
||||
|
17
include/linux/nfs_common.h
Normal file
17
include/linux/nfs_common.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This file contains constants and methods used by both NFS client and server.
|
||||
*/
|
||||
#ifndef _LINUX_NFS_COMMON_H
|
||||
#define _LINUX_NFS_COMMON_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <uapi/linux/nfs.h>
|
||||
|
||||
/* Mapping from NFS error code to "errno" error code. */
|
||||
#define errno_NFSERR_IO EIO
|
||||
|
||||
int nfs_stat_to_errno(enum nfs_stat status);
|
||||
int nfs4_stat_to_errno(int stat);
|
||||
|
||||
#endif /* _LINUX_NFS_COMMON_H */
|
@ -8,6 +8,7 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/nfs_xdr.h>
|
||||
#include <linux/sunrpc/xprt.h>
|
||||
#include <linux/nfslocalio.h>
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/refcount.h>
|
||||
@ -49,6 +50,7 @@ struct nfs_client {
|
||||
#define NFS_CS_DS 7 /* - Server is a DS */
|
||||
#define NFS_CS_REUSEPORT 8 /* - reuse src port on reconnect */
|
||||
#define NFS_CS_PNFS 9 /* - Server used for pnfs */
|
||||
#define NFS_CS_LOCAL_IO 10 /* - client is local */
|
||||
struct sockaddr_storage cl_addr; /* server identifier */
|
||||
size_t cl_addrlen;
|
||||
char * cl_hostname; /* hostname of server */
|
||||
@ -125,6 +127,13 @@ struct nfs_client {
|
||||
struct net *cl_net;
|
||||
struct list_head pending_cb_stateids;
|
||||
struct rcu_head rcu;
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
struct timespec64 cl_nfssvc_boot;
|
||||
seqlock_t cl_boot_lock;
|
||||
nfs_uuid_t cl_uuid;
|
||||
spinlock_t cl_localio_lock;
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -158,6 +167,7 @@ struct nfs_server {
|
||||
#define NFS_MOUNT_WRITE_WAIT 0x02000000
|
||||
#define NFS_MOUNT_TRUNK_DISCOVERY 0x04000000
|
||||
#define NFS_MOUNT_SHUTDOWN 0x08000000
|
||||
#define NFS_MOUNT_NO_ALIGNWRITE 0x10000000
|
||||
|
||||
unsigned int fattr_valid; /* Valid attributes */
|
||||
unsigned int caps; /* server capabilities */
|
||||
@ -234,8 +244,7 @@ struct nfs_server {
|
||||
/* the following fields are protected by nfs_client->cl_lock */
|
||||
struct rb_root state_owners;
|
||||
#endif
|
||||
struct ida openowner_id;
|
||||
struct ida lockowner_id;
|
||||
atomic64_t owner_ctr;
|
||||
struct list_head state_owners_lru;
|
||||
struct list_head layouts;
|
||||
struct list_head delegations;
|
||||
|
@ -446,7 +446,7 @@ struct nfs42_clone_res {
|
||||
|
||||
struct stateowner_id {
|
||||
__u64 create_time;
|
||||
__u32 uniquifier;
|
||||
__u64 uniquifier;
|
||||
};
|
||||
|
||||
struct nfs4_open_delegation {
|
||||
@ -1853,6 +1853,24 @@ struct nfs_rpc_ops {
|
||||
void (*disable_swap)(struct inode *inode);
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper functions used by NFS client and/or server
|
||||
*/
|
||||
static inline void encode_opaque_fixed(struct xdr_stream *xdr,
|
||||
const void *buf, size_t len)
|
||||
{
|
||||
WARN_ON_ONCE(xdr_stream_encode_opaque_fixed(xdr, buf, len) < 0);
|
||||
}
|
||||
|
||||
static inline int decode_opaque_fixed(struct xdr_stream *xdr,
|
||||
void *buf, size_t len)
|
||||
{
|
||||
ssize_t ret = xdr_stream_decode_opaque_fixed(xdr, buf, len);
|
||||
if (unlikely(ret < 0))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function vectors etc. for the NFS client
|
||||
*/
|
||||
@ -1866,4 +1884,4 @@ extern const struct rpc_version nfs_version4;
|
||||
extern const struct rpc_version nfsacl_version3;
|
||||
extern const struct rpc_program nfsacl_program;
|
||||
|
||||
#endif
|
||||
#endif /* _LINUX_NFS_XDR_H */
|
||||
|
74
include/linux/nfslocalio.h
Normal file
74
include/linux/nfslocalio.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
|
||||
* Copyright (C) 2024 NeilBrown <neilb@suse.de>
|
||||
*/
|
||||
#ifndef __LINUX_NFSLOCALIO_H
|
||||
#define __LINUX_NFSLOCALIO_H
|
||||
|
||||
/* nfsd_file structure is purposely kept opaque to NFS client */
|
||||
struct nfsd_file;
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svcauth.h>
|
||||
#include <linux/nfs.h>
|
||||
#include <net/net_namespace.h>
|
||||
|
||||
/*
|
||||
* Useful to allow a client to negotiate if localio
|
||||
* possible with its server.
|
||||
*
|
||||
* See Documentation/filesystems/nfs/localio.rst for more detail.
|
||||
*/
|
||||
typedef struct {
|
||||
uuid_t uuid;
|
||||
struct list_head list;
|
||||
struct net __rcu *net; /* nfsd's network namespace */
|
||||
struct auth_domain *dom; /* auth_domain for localio */
|
||||
} nfs_uuid_t;
|
||||
|
||||
void nfs_uuid_begin(nfs_uuid_t *);
|
||||
void nfs_uuid_end(nfs_uuid_t *);
|
||||
void nfs_uuid_is_local(const uuid_t *, struct list_head *,
|
||||
struct net *, struct auth_domain *, struct module *);
|
||||
void nfs_uuid_invalidate_clients(struct list_head *list);
|
||||
void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid);
|
||||
|
||||
/* localio needs to map filehandle -> struct nfsd_file */
|
||||
extern struct nfsd_file *
|
||||
nfsd_open_local_fh(struct net *, struct auth_domain *, struct rpc_clnt *,
|
||||
const struct cred *, const struct nfs_fh *,
|
||||
const fmode_t) __must_hold(rcu);
|
||||
|
||||
struct nfsd_localio_operations {
|
||||
bool (*nfsd_serv_try_get)(struct net *);
|
||||
void (*nfsd_serv_put)(struct net *);
|
||||
struct nfsd_file *(*nfsd_open_local_fh)(struct net *,
|
||||
struct auth_domain *,
|
||||
struct rpc_clnt *,
|
||||
const struct cred *,
|
||||
const struct nfs_fh *,
|
||||
const fmode_t);
|
||||
void (*nfsd_file_put_local)(struct nfsd_file *);
|
||||
struct file *(*nfsd_file_file)(struct nfsd_file *);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
extern void nfsd_localio_ops_init(void);
|
||||
extern const struct nfsd_localio_operations *nfs_to;
|
||||
|
||||
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
|
||||
struct rpc_clnt *, const struct cred *,
|
||||
const struct nfs_fh *, const fmode_t);
|
||||
|
||||
#else /* CONFIG_NFS_LOCALIO */
|
||||
static inline void nfsd_localio_ops_init(void)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
|
||||
#endif /* __LINUX_NFSLOCALIO_H */
|
@ -151,13 +151,15 @@ struct rpc_task_setup {
|
||||
#define RPC_WAS_SENT(t) ((t)->tk_flags & RPC_TASK_SENT)
|
||||
#define RPC_IS_MOVEABLE(t) ((t)->tk_flags & RPC_TASK_MOVEABLE)
|
||||
|
||||
#define RPC_TASK_RUNNING 0
|
||||
#define RPC_TASK_QUEUED 1
|
||||
#define RPC_TASK_ACTIVE 2
|
||||
#define RPC_TASK_NEED_XMIT 3
|
||||
#define RPC_TASK_NEED_RECV 4
|
||||
#define RPC_TASK_MSG_PIN_WAIT 5
|
||||
#define RPC_TASK_SIGNALLED 6
|
||||
enum {
|
||||
RPC_TASK_RUNNING,
|
||||
RPC_TASK_QUEUED,
|
||||
RPC_TASK_ACTIVE,
|
||||
RPC_TASK_NEED_XMIT,
|
||||
RPC_TASK_NEED_RECV,
|
||||
RPC_TASK_MSG_PIN_WAIT,
|
||||
RPC_TASK_SIGNALLED,
|
||||
};
|
||||
|
||||
#define rpc_test_and_set_running(t) \
|
||||
test_and_set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)
|
||||
|
@ -67,9 +67,10 @@ enum {
|
||||
* We currently do not support more than one RPC program per daemon.
|
||||
*/
|
||||
struct svc_serv {
|
||||
struct svc_program * sv_program; /* RPC program */
|
||||
struct svc_program * sv_programs; /* RPC programs */
|
||||
struct svc_stat * sv_stats; /* RPC statistics */
|
||||
spinlock_t sv_lock;
|
||||
unsigned int sv_nprogs; /* Number of sv_programs */
|
||||
unsigned int sv_nrthreads; /* # of server threads */
|
||||
unsigned int sv_maxconn; /* max connections allowed or
|
||||
* '0' causing max to be based
|
||||
@ -360,10 +361,9 @@ struct svc_process_info {
|
||||
};
|
||||
|
||||
/*
|
||||
* List of RPC programs on the same transport endpoint
|
||||
* RPC program - an array of these can use the same transport endpoint
|
||||
*/
|
||||
struct svc_program {
|
||||
struct svc_program * pg_next; /* other programs (same xprt) */
|
||||
u32 pg_prog; /* program number */
|
||||
unsigned int pg_lovers; /* lowest version */
|
||||
unsigned int pg_hivers; /* highest version */
|
||||
@ -441,6 +441,7 @@ bool svc_rqst_replace_page(struct svc_rqst *rqstp,
|
||||
void svc_rqst_release_pages(struct svc_rqst *rqstp);
|
||||
void svc_exit_thread(struct svc_rqst *);
|
||||
struct svc_serv * svc_create_pooled(struct svc_program *prog,
|
||||
unsigned int nprog,
|
||||
struct svc_stat *stats,
|
||||
unsigned int bufsize,
|
||||
int (*threadfn)(void *data));
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
#include <linux/sunrpc/cache.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/stringhash.h>
|
||||
#include <linux/cred.h>
|
||||
@ -157,6 +158,10 @@ extern enum svc_auth_status svc_set_client(struct svc_rqst *rqstp);
|
||||
extern int svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops);
|
||||
extern void svc_auth_unregister(rpc_authflavor_t flavor);
|
||||
|
||||
extern void svcauth_map_clnt_to_svc_cred_local(struct rpc_clnt *clnt,
|
||||
const struct cred *,
|
||||
struct svc_cred *);
|
||||
|
||||
extern struct auth_domain *unix_domain_find(char *name);
|
||||
extern void auth_domain_put(struct auth_domain *item);
|
||||
extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *new);
|
||||
|
@ -731,11 +731,10 @@ static bool cache_defer_req(struct cache_req *req, struct cache_head *item)
|
||||
static void cache_revisit_request(struct cache_head *item)
|
||||
{
|
||||
struct cache_deferred_req *dreq;
|
||||
struct list_head pending;
|
||||
struct hlist_node *tmp;
|
||||
int hash = DFR_HASH(item);
|
||||
LIST_HEAD(pending);
|
||||
|
||||
INIT_LIST_HEAD(&pending);
|
||||
spin_lock(&cache_defer_lock);
|
||||
|
||||
hlist_for_each_entry_safe(dreq, tmp, &cache_defer_hash[hash], hash)
|
||||
@ -756,10 +755,8 @@ static void cache_revisit_request(struct cache_head *item)
|
||||
void cache_clean_deferred(void *owner)
|
||||
{
|
||||
struct cache_deferred_req *dreq, *tmp;
|
||||
struct list_head pending;
|
||||
LIST_HEAD(pending);
|
||||
|
||||
|
||||
INIT_LIST_HEAD(&pending);
|
||||
spin_lock(&cache_defer_lock);
|
||||
|
||||
list_for_each_entry_safe(dreq, tmp, &cache_defer_list, recent) {
|
||||
@ -1085,9 +1082,8 @@ static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
|
||||
{
|
||||
struct cache_queue *cq, *tmp;
|
||||
struct cache_request *cr;
|
||||
struct list_head dequeued;
|
||||
LIST_HEAD(dequeued);
|
||||
|
||||
INIT_LIST_HEAD(&dequeued);
|
||||
spin_lock(&queue_lock);
|
||||
list_for_each_entry_safe(cq, tmp, &detail->queue, list)
|
||||
if (!cq->reader) {
|
||||
|
@ -48,13 +48,8 @@
|
||||
# define RPCDBG_FACILITY RPCDBG_CALL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* All RPC clients are linked into this list
|
||||
*/
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(destroy_wait);
|
||||
|
||||
|
||||
static void call_start(struct rpc_task *task);
|
||||
static void call_reserve(struct rpc_task *task);
|
||||
static void call_reserveresult(struct rpc_task *task);
|
||||
@ -546,7 +541,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
|
||||
.connect_timeout = args->connect_timeout,
|
||||
.reconnect_timeout = args->reconnect_timeout,
|
||||
};
|
||||
char servername[48];
|
||||
char servername[RPC_MAXNETNAMELEN];
|
||||
struct rpc_clnt *clnt;
|
||||
int i;
|
||||
|
||||
@ -1893,12 +1888,6 @@ call_allocate(struct rpc_task *task)
|
||||
if (req->rq_buffer)
|
||||
return;
|
||||
|
||||
if (proc->p_proc != 0) {
|
||||
BUG_ON(proc->p_arglen == 0);
|
||||
if (proc->p_decode != NULL)
|
||||
BUG_ON(proc->p_replen == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the size (in quads) of the RPC call
|
||||
* and reply headers, and convert both values
|
||||
|
@ -440,10 +440,11 @@ EXPORT_SYMBOL_GPL(svc_rpcb_cleanup);
|
||||
|
||||
static int svc_uses_rpcbind(struct svc_serv *serv)
|
||||
{
|
||||
struct svc_program *progp;
|
||||
unsigned int i;
|
||||
unsigned int p, i;
|
||||
|
||||
for (p = 0; p < serv->sv_nprogs; p++) {
|
||||
struct svc_program *progp = &serv->sv_programs[p];
|
||||
|
||||
for (progp = serv->sv_program; progp; progp = progp->pg_next) {
|
||||
for (i = 0; i < progp->pg_nvers; i++) {
|
||||
if (progp->pg_vers[i] == NULL)
|
||||
continue;
|
||||
@ -480,7 +481,7 @@ __svc_init_bc(struct svc_serv *serv)
|
||||
* Create an RPC service
|
||||
*/
|
||||
static struct svc_serv *
|
||||
__svc_create(struct svc_program *prog, struct svc_stat *stats,
|
||||
__svc_create(struct svc_program *prog, int nprogs, struct svc_stat *stats,
|
||||
unsigned int bufsize, int npools, int (*threadfn)(void *data))
|
||||
{
|
||||
struct svc_serv *serv;
|
||||
@ -491,7 +492,8 @@ __svc_create(struct svc_program *prog, struct svc_stat *stats,
|
||||
if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL)))
|
||||
return NULL;
|
||||
serv->sv_name = prog->pg_name;
|
||||
serv->sv_program = prog;
|
||||
serv->sv_programs = prog;
|
||||
serv->sv_nprogs = nprogs;
|
||||
serv->sv_stats = stats;
|
||||
if (bufsize > RPCSVC_MAXPAYLOAD)
|
||||
bufsize = RPCSVC_MAXPAYLOAD;
|
||||
@ -499,17 +501,18 @@ __svc_create(struct svc_program *prog, struct svc_stat *stats,
|
||||
serv->sv_max_mesg = roundup(serv->sv_max_payload + PAGE_SIZE, PAGE_SIZE);
|
||||
serv->sv_threadfn = threadfn;
|
||||
xdrsize = 0;
|
||||
while (prog) {
|
||||
prog->pg_lovers = prog->pg_nvers-1;
|
||||
for (vers=0; vers<prog->pg_nvers ; vers++)
|
||||
if (prog->pg_vers[vers]) {
|
||||
prog->pg_hivers = vers;
|
||||
if (prog->pg_lovers > vers)
|
||||
prog->pg_lovers = vers;
|
||||
if (prog->pg_vers[vers]->vs_xdrsize > xdrsize)
|
||||
xdrsize = prog->pg_vers[vers]->vs_xdrsize;
|
||||
for (i = 0; i < nprogs; i++) {
|
||||
struct svc_program *progp = &prog[i];
|
||||
|
||||
progp->pg_lovers = progp->pg_nvers-1;
|
||||
for (vers = 0; vers < progp->pg_nvers ; vers++)
|
||||
if (progp->pg_vers[vers]) {
|
||||
progp->pg_hivers = vers;
|
||||
if (progp->pg_lovers > vers)
|
||||
progp->pg_lovers = vers;
|
||||
if (progp->pg_vers[vers]->vs_xdrsize > xdrsize)
|
||||
xdrsize = progp->pg_vers[vers]->vs_xdrsize;
|
||||
}
|
||||
prog = prog->pg_next;
|
||||
}
|
||||
serv->sv_xdrsize = xdrsize;
|
||||
INIT_LIST_HEAD(&serv->sv_tempsocks);
|
||||
@ -558,13 +561,14 @@ __svc_create(struct svc_program *prog, struct svc_stat *stats,
|
||||
struct svc_serv *svc_create(struct svc_program *prog, unsigned int bufsize,
|
||||
int (*threadfn)(void *data))
|
||||
{
|
||||
return __svc_create(prog, NULL, bufsize, 1, threadfn);
|
||||
return __svc_create(prog, 1, NULL, bufsize, 1, threadfn);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_create);
|
||||
|
||||
/**
|
||||
* svc_create_pooled - Create an RPC service with pooled threads
|
||||
* @prog: the RPC program the new service will handle
|
||||
* @prog: Array of RPC programs the new service will handle
|
||||
* @nprogs: Number of programs in the array
|
||||
* @stats: the stats struct if desired
|
||||
* @bufsize: maximum message size for @prog
|
||||
* @threadfn: a function to service RPC requests for @prog
|
||||
@ -572,6 +576,7 @@ EXPORT_SYMBOL_GPL(svc_create);
|
||||
* Returns an instantiated struct svc_serv object or NULL.
|
||||
*/
|
||||
struct svc_serv *svc_create_pooled(struct svc_program *prog,
|
||||
unsigned int nprogs,
|
||||
struct svc_stat *stats,
|
||||
unsigned int bufsize,
|
||||
int (*threadfn)(void *data))
|
||||
@ -579,7 +584,7 @@ struct svc_serv *svc_create_pooled(struct svc_program *prog,
|
||||
struct svc_serv *serv;
|
||||
unsigned int npools = svc_pool_map_get();
|
||||
|
||||
serv = __svc_create(prog, stats, bufsize, npools, threadfn);
|
||||
serv = __svc_create(prog, nprogs, stats, bufsize, npools, threadfn);
|
||||
if (!serv)
|
||||
goto out_err;
|
||||
serv->sv_is_pooled = true;
|
||||
@ -602,16 +607,16 @@ svc_destroy(struct svc_serv **servp)
|
||||
|
||||
*servp = NULL;
|
||||
|
||||
dprintk("svc: svc_destroy(%s)\n", serv->sv_program->pg_name);
|
||||
dprintk("svc: svc_destroy(%s)\n", serv->sv_programs->pg_name);
|
||||
timer_shutdown_sync(&serv->sv_temptimer);
|
||||
|
||||
/*
|
||||
* Remaining transports at this point are not expected.
|
||||
*/
|
||||
WARN_ONCE(!list_empty(&serv->sv_permsocks),
|
||||
"SVC: permsocks remain for %s\n", serv->sv_program->pg_name);
|
||||
"SVC: permsocks remain for %s\n", serv->sv_programs->pg_name);
|
||||
WARN_ONCE(!list_empty(&serv->sv_tempsocks),
|
||||
"SVC: tempsocks remain for %s\n", serv->sv_program->pg_name);
|
||||
"SVC: tempsocks remain for %s\n", serv->sv_programs->pg_name);
|
||||
|
||||
cache_clean_deferred(serv);
|
||||
|
||||
@ -1148,15 +1153,16 @@ int svc_register(const struct svc_serv *serv, struct net *net,
|
||||
const int family, const unsigned short proto,
|
||||
const unsigned short port)
|
||||
{
|
||||
struct svc_program *progp;
|
||||
unsigned int i;
|
||||
unsigned int p, i;
|
||||
int error = 0;
|
||||
|
||||
WARN_ON_ONCE(proto == 0 && port == 0);
|
||||
if (proto == 0 && port == 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (progp = serv->sv_program; progp; progp = progp->pg_next) {
|
||||
for (p = 0; p < serv->sv_nprogs; p++) {
|
||||
struct svc_program *progp = &serv->sv_programs[p];
|
||||
|
||||
for (i = 0; i < progp->pg_nvers; i++) {
|
||||
|
||||
error = progp->pg_rpcbind_set(net, progp, i,
|
||||
@ -1208,13 +1214,14 @@ static void __svc_unregister(struct net *net, const u32 program, const u32 versi
|
||||
static void svc_unregister(const struct svc_serv *serv, struct net *net)
|
||||
{
|
||||
struct sighand_struct *sighand;
|
||||
struct svc_program *progp;
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
unsigned int p, i;
|
||||
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
|
||||
for (progp = serv->sv_program; progp; progp = progp->pg_next) {
|
||||
for (p = 0; p < serv->sv_nprogs; p++) {
|
||||
struct svc_program *progp = &serv->sv_programs[p];
|
||||
|
||||
for (i = 0; i < progp->pg_nvers; i++) {
|
||||
if (progp->pg_vers[i] == NULL)
|
||||
continue;
|
||||
@ -1320,7 +1327,7 @@ svc_process_common(struct svc_rqst *rqstp)
|
||||
struct svc_process_info process;
|
||||
enum svc_auth_status auth_res;
|
||||
unsigned int aoffset;
|
||||
int rc;
|
||||
int pr, rc;
|
||||
__be32 *p;
|
||||
|
||||
/* Will be turned off only when NFSv4 Sessions are used */
|
||||
@ -1344,9 +1351,12 @@ svc_process_common(struct svc_rqst *rqstp)
|
||||
rqstp->rq_vers = be32_to_cpup(p++);
|
||||
rqstp->rq_proc = be32_to_cpup(p);
|
||||
|
||||
for (progp = serv->sv_program; progp; progp = progp->pg_next)
|
||||
for (pr = 0; pr < serv->sv_nprogs; pr++) {
|
||||
progp = &serv->sv_programs[pr];
|
||||
|
||||
if (rqstp->rq_prog == progp->pg_prog)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode auth data, and add verifier to reply buffer.
|
||||
|
@ -268,7 +268,7 @@ static int _svc_xprt_create(struct svc_serv *serv, const char *xprt_name,
|
||||
spin_unlock(&svc_xprt_class_lock);
|
||||
newxprt = xcl->xcl_ops->xpo_create(serv, net, sap, len, flags);
|
||||
if (IS_ERR(newxprt)) {
|
||||
trace_svc_xprt_create_err(serv->sv_program->pg_name,
|
||||
trace_svc_xprt_create_err(serv->sv_programs->pg_name,
|
||||
xcl->xcl_name, sap, len,
|
||||
newxprt);
|
||||
module_put(xcl->xcl_owner);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/sunrpc/svcauth.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/user_namespace.h>
|
||||
|
||||
#include <trace/events/sunrpc.h>
|
||||
|
||||
@ -175,6 +176,33 @@ rpc_authflavor_t svc_auth_flavor(struct svc_rqst *rqstp)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_auth_flavor);
|
||||
|
||||
/**
|
||||
* svcauth_map_clnt_to_svc_cred_local - maps a generic cred
|
||||
* to a svc_cred suitable for use in nfsd.
|
||||
* @clnt: rpc_clnt associated with nfs client
|
||||
* @cred: generic cred associated with nfs client
|
||||
* @svc: returned svc_cred that is suitable for use in nfsd
|
||||
*/
|
||||
void svcauth_map_clnt_to_svc_cred_local(struct rpc_clnt *clnt,
|
||||
const struct cred *cred,
|
||||
struct svc_cred *svc)
|
||||
{
|
||||
struct user_namespace *userns = clnt->cl_cred ?
|
||||
clnt->cl_cred->user_ns : &init_user_ns;
|
||||
|
||||
memset(svc, 0, sizeof(struct svc_cred));
|
||||
|
||||
svc->cr_uid = KUIDT_INIT(from_kuid_munged(userns, cred->fsuid));
|
||||
svc->cr_gid = KGIDT_INIT(from_kgid_munged(userns, cred->fsgid));
|
||||
svc->cr_flavor = clnt->cl_auth->au_flavor;
|
||||
if (cred->group_info)
|
||||
svc->cr_group_info = get_group_info(cred->group_info);
|
||||
/* These aren't relevant for local (network is bypassed) */
|
||||
svc->cr_principal = NULL;
|
||||
svc->cr_gss_mech = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svcauth_map_clnt_to_svc_cred_local);
|
||||
|
||||
/**************************************************
|
||||
* 'auth_domains' are stored in a hash table indexed by name.
|
||||
* When the last reference to an 'auth_domain' is dropped,
|
||||
|
@ -697,7 +697,8 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
|
||||
rqstp->rq_auth_stat = rpc_autherr_badcred;
|
||||
ipm = ip_map_cached_get(xprt);
|
||||
if (ipm == NULL)
|
||||
ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
|
||||
ipm = __ip_map_lookup(sn->ip_map_cache,
|
||||
rqstp->rq_server->sv_programs->pg_class,
|
||||
&sin6->sin6_addr);
|
||||
|
||||
if (ipm == NULL)
|
||||
|
Loading…
Reference in New Issue
Block a user