summaryrefslogtreecommitdiff
path: root/include/linux/sunrpc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-20 10:44:02 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-20 10:44:02 -0700
commit36d179fd6bea35698d53444b7bd3025fa3788266 (patch)
tree399ec5f312f24b6d6eadc60125960e1d53216086 /include/linux/sunrpc
parentc1f49dea2b8f335813d3b348fd39117fb8efb428 (diff)
parentd644a698de12e996778657f65a4608299368e138 (diff)
Merge tag 'nfsd-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
Pull nfsd updates from Chuck Lever: - filehandle signing to defend against filehandle-guessing attacks (Benjamin Coddington) The server now appends a SipHash-2-4 MAC to each filehandle when the new "sign_fh" export option is enabled. NFSD then verifies filehandles received from clients against the expected MAC; mismatches return NFS error STALE - convert the entire NLMv4 server-side XDR layer from hand-written C to xdrgen-generated code, spanning roughly thirty patches (Chuck Lever) XDR functions are generally boilerplate code and are easy to get wrong. The goals of this conversion are improved memory safety, lower maintenance burden, and groundwork for eventual Rust code generation for these functions. - improve pNFS block/SCSI layout robustness with two related changes (Dai Ngo) SCSI persistent reservation fencing is now tracked per client and per device via an xarray, to avoid both redundant preempt operations on devices already fenced and a potential NFSD deadlock when all nfsd threads are waiting for a layout return. - scalability and infrastructure improvements Sincere thanks to all contributors, reviewers, testers, and bug reporters who participated in the v7.1 NFSD development cycle. * tag 'nfsd-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (83 commits) NFSD: Docs: clean up pnfs server timeout docs nfsd: fix comment typo in nfsxdr nfsd: fix comment typo in nfs3xdr NFSD: convert callback RPC program to per-net namespace NFSD: use per-operation statidx for callback procedures svcrdma: Use contiguous pages for RDMA Read sink buffers SUNRPC: Add svc_rqst_page_release() helper SUNRPC: xdr.h: fix all kernel-doc warnings svcrdma: Factor out WR chain linking into helper svcrdma: Add Write chunk WRs to the RPC's Send WR chain svcrdma: Clean up use of rdma->sc_pd->device svcrdma: Clean up use of rdma->sc_pd->device in Receive paths svcrdma: Add fair queuing for Send Queue access SUNRPC: Optimize rq_respages allocation in svc_alloc_arg SUNRPC: Track consumed rq_pages entries svcrdma: preserve rq_next_page in svc_rdma_save_io_pages SUNRPC: Handle NULL entries in svc_rqst_release_pages SUNRPC: Allocate a separate Reply page array SUNRPC: Tighten bounds checking in svc_rqst_replace_page NFSD: Sign filehandles ...
Diffstat (limited to 'include/linux/sunrpc')
-rw-r--r--include/linux/sunrpc/cache.h7
-rw-r--r--include/linux/sunrpc/debug.h10
-rw-r--r--include/linux/sunrpc/sched.h3
-rw-r--r--include/linux/sunrpc/svc.h82
-rw-r--r--include/linux/sunrpc/svc_rdma.h23
-rw-r--r--include/linux/sunrpc/xdr.h48
-rw-r--r--include/linux/sunrpc/xdrgen/nlm4.h233
7 files changed, 345 insertions, 61 deletions
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index e783132e481f..b1e595c2615b 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -16,6 +16,7 @@
#include <linux/atomic.h>
#include <linux/kstrtox.h>
#include <linux/proc_fs.h>
+#include <linux/wait.h>
/*
* Each cache requires:
@@ -112,7 +113,11 @@ struct cache_detail {
int entries;
/* fields for communication over channel */
- struct list_head queue;
+ struct list_head requests;
+ struct list_head readers;
+ spinlock_t queue_lock;
+ wait_queue_head_t queue_wait;
+ u64 next_seqno;
atomic_t writers; /* how many time is /channel open */
time64_t last_close; /* if no writers, when did last close */
diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h
index eb4bd62df319..ab61bed2f7af 100644
--- a/include/linux/sunrpc/debug.h
+++ b/include/linux/sunrpc/debug.h
@@ -38,6 +38,8 @@ extern unsigned int nlm_debug;
do { \
ifdebug(fac) \
__sunrpc_printk(fmt, ##__VA_ARGS__); \
+ else \
+ no_printk(fmt, ##__VA_ARGS__); \
} while (0)
# define dfprintk_rcu(fac, fmt, ...) \
@@ -46,15 +48,15 @@ do { \
rcu_read_lock(); \
__sunrpc_printk(fmt, ##__VA_ARGS__); \
rcu_read_unlock(); \
+ } else { \
+ no_printk(fmt, ##__VA_ARGS__); \
} \
} while (0)
-# define RPC_IFDEBUG(x) x
#else
# define ifdebug(fac) if (0)
-# define dfprintk(fac, fmt, ...) do {} while (0)
-# define dfprintk_rcu(fac, fmt, ...) do {} while (0)
-# define RPC_IFDEBUG(x)
+# define dfprintk(fac, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
+# define dfprintk_rcu(fac, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif
/*
diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
index ccba79ebf893..0dbdf3722537 100644
--- a/include/linux/sunrpc/sched.h
+++ b/include/linux/sunrpc/sched.h
@@ -95,10 +95,7 @@ struct rpc_task {
int tk_rpc_status; /* Result of last RPC operation */
unsigned short tk_flags; /* misc flags */
unsigned short tk_timeouts; /* maj timeouts */
-
-#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) || IS_ENABLED(CONFIG_TRACEPOINTS)
unsigned short tk_pid; /* debugging aid */
-#endif
unsigned char tk_priority : 2,/* Task priority */
tk_garb_retry : 2,
tk_cred_retry : 2;
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index a11acf5cd63b..4be6204f6630 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -134,25 +134,37 @@ enum {
extern u32 svc_max_payload(const struct svc_rqst *rqstp);
/*
- * RPC Requests and replies are stored in one or more pages.
- * We maintain an array of pages for each server thread.
- * Requests are copied into these pages as they arrive. Remaining
- * pages are available to write the reply into.
+ * RPC Call and Reply messages each have their own page array.
+ * rq_pages holds the incoming Call message; rq_respages holds
+ * the outgoing Reply message. Both arrays are sized to
+ * svc_serv_maxpages() entries and are allocated dynamically.
*
- * Pages are sent using ->sendmsg with MSG_SPLICE_PAGES so each server thread
- * needs to allocate more to replace those used in sending. To help keep track
- * of these pages we have a receive list where all pages initialy live, and a
- * send list where pages are moved to when there are to be part of a reply.
+ * Pages are sent using ->sendmsg with MSG_SPLICE_PAGES so each
+ * server thread needs to allocate more to replace those used in
+ * sending.
*
- * We use xdr_buf for holding responses as it fits well with NFS
- * read responses (that have a header, and some data pages, and possibly
- * a tail) and means we can share some client side routines.
+ * rq_pages request page contract:
*
- * The xdr_buf.head kvec always points to the first page in the rq_*pages
- * list. The xdr_buf.pages pointer points to the second page on that
- * list. xdr_buf.tail points to the end of the first page.
- * This assumes that the non-page part of an rpc reply will fit
- * in a page - NFSd ensures this. lockd also has no trouble.
+ * Transport receive paths that move request data pages out of
+ * rq_pages -- TCP multi-fragment reassembly (svc_tcp_save_pages)
+ * and RDMA Read I/O (svc_rdma_clear_rqst_pages) -- NULL those
+ * entries to prevent svc_rqst_release_pages() from freeing pages
+ * still in transport use, and set rq_pages_nfree to the count.
+ * svc_alloc_arg() refills only that many rq_pages entries.
+ *
+ * For rq_respages, svc_rqst_release_pages() NULLs entries in
+ * [rq_respages, rq_next_page) after each RPC. svc_alloc_arg()
+ * refills only that range.
+ *
+ * xdr_buf holds responses; the structure fits NFS read responses
+ * (header, data pages, optional tail) and enables sharing of
+ * client-side routines.
+ *
+ * The xdr_buf.head kvec always points to the first page in the
+ * rq_*pages list. The xdr_buf.pages pointer points to the second
+ * page on that list. xdr_buf.tail points to the end of the first
+ * page. This assumes that the non-page part of an rpc reply will
+ * fit in a page - NFSd ensures this. lockd also has no trouble.
*/
/**
@@ -162,10 +174,10 @@ extern u32 svc_max_payload(const struct svc_rqst *rqstp);
* Returns a count of pages or vectors that can hold the maximum
* size RPC message for @serv.
*
- * Each request/reply pair can have at most one "payload", plus two
- * pages, one for the request, and one for the reply.
- * nfsd_splice_actor() might need an extra page when a READ payload
- * is not page-aligned.
+ * Each page array can hold at most one payload plus two
+ * overhead pages (one for the RPC header, one for tail data).
+ * nfsd_splice_actor() might need an extra page when a READ
+ * payload is not page-aligned.
*/
static inline unsigned long svc_serv_maxpages(const struct svc_serv *serv)
{
@@ -175,6 +187,9 @@ static inline unsigned long svc_serv_maxpages(const struct svc_serv *serv)
/*
* The context of a single thread, including the request currently being
* processed.
+ *
+ * RPC programs are free to use rq_private to stash thread-local information.
+ * The sunrpc layer will not access it.
*/
struct svc_rqst {
struct list_head rq_all; /* all threads list */
@@ -201,11 +216,12 @@ struct svc_rqst {
struct xdr_stream rq_res_stream;
struct folio *rq_scratch_folio;
struct xdr_buf rq_res;
- unsigned long rq_maxpages; /* num of entries in rq_pages */
- struct page * *rq_pages;
- struct page * *rq_respages; /* points into rq_pages */
+ unsigned long rq_maxpages; /* entries per page array */
+ unsigned long rq_pages_nfree; /* rq_pages entries NULLed by transport */
+ struct page * *rq_pages; /* Call buffer pages */
+ struct page * *rq_respages; /* Reply buffer pages */
struct page * *rq_next_page; /* next reply page to use */
- struct page * *rq_page_end; /* one past the last page */
+ struct page * *rq_page_end; /* one past the last reply page */
struct folio_batch rq_fbatch;
struct bio_vec *rq_bvec;
@@ -215,7 +231,6 @@ struct svc_rqst {
u32 rq_vers; /* program version */
u32 rq_proc; /* procedure number */
u32 rq_prot; /* IP protocol */
- int rq_cachetype; /* catering to nfsd */
unsigned long rq_flags; /* flags field */
ktime_t rq_qtime; /* enqueue time */
@@ -251,7 +266,7 @@ struct svc_rqst {
unsigned long bc_to_initval;
unsigned int bc_to_retries;
unsigned int rq_status_counter; /* RPC processing counter */
- void **rq_lease_breaker; /* The v4 client breaking a lease */
+ void *rq_private; /* For use by the service thread */
};
/* bits for rq_flags */
@@ -483,6 +498,21 @@ int svc_generic_rpcbind_set(struct net *net,
#define RPC_MAX_ADDRBUFLEN (63U)
+/**
+ * svc_rqst_page_release - release a page associated with an RPC transaction
+ * @rqstp: RPC transaction context
+ * @page: page to release
+ *
+ * Released pages are batched and freed together, reducing
+ * allocator pressure under heavy RPC workloads.
+ */
+static inline void svc_rqst_page_release(struct svc_rqst *rqstp,
+ struct page *page)
+{
+ if (!folio_batch_add(&rqstp->rq_fbatch, page_folio(page)))
+ __folio_batch_release(&rqstp->rq_fbatch);
+}
+
/*
* When we want to reduce the size of the reserved space in the response
* buffer, we need to take into account the size of any checksum data that
diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h
index 57f4fd94166a..df6e08aaad57 100644
--- a/include/linux/sunrpc/svc_rdma.h
+++ b/include/linux/sunrpc/svc_rdma.h
@@ -84,6 +84,9 @@ struct svcxprt_rdma {
atomic_t sc_sq_avail; /* SQEs ready to be consumed */
unsigned int sc_sq_depth; /* Depth of SQ */
+ atomic_t sc_sq_ticket_head; /* Next ticket to issue */
+ atomic_t sc_sq_ticket_tail; /* Ticket currently serving */
+ wait_queue_head_t sc_sq_ticket_wait; /* Ticket ordering waitlist */
__be32 sc_fc_credits; /* Forward credits */
u32 sc_max_requests; /* Max requests */
u32 sc_max_bc_requests;/* Backward credits */
@@ -213,6 +216,7 @@ struct svc_rdma_recv_ctxt {
*/
struct svc_rdma_write_info {
struct svcxprt_rdma *wi_rdma;
+ struct list_head wi_list;
const struct svc_rdma_chunk *wi_chunk;
@@ -241,7 +245,10 @@ struct svc_rdma_send_ctxt {
struct ib_cqe sc_cqe;
struct xdr_buf sc_hdrbuf;
struct xdr_stream sc_stream;
+
+ struct list_head sc_write_info_list;
struct svc_rdma_write_info sc_reply_info;
+
void *sc_xprt_buf;
int sc_page_count;
int sc_cur_sge_no;
@@ -274,11 +281,14 @@ extern void svc_rdma_cc_init(struct svcxprt_rdma *rdma,
extern void svc_rdma_cc_release(struct svcxprt_rdma *rdma,
struct svc_rdma_chunk_ctxt *cc,
enum dma_data_direction dir);
+extern void svc_rdma_write_chunk_release(struct svcxprt_rdma *rdma,
+ struct svc_rdma_send_ctxt *ctxt);
extern void svc_rdma_reply_chunk_release(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *ctxt);
-extern int svc_rdma_send_write_list(struct svcxprt_rdma *rdma,
- const struct svc_rdma_recv_ctxt *rctxt,
- const struct xdr_buf *xdr);
+extern int svc_rdma_prepare_write_list(struct svcxprt_rdma *rdma,
+ const struct svc_rdma_recv_ctxt *rctxt,
+ struct svc_rdma_send_ctxt *sctxt,
+ const struct xdr_buf *xdr);
extern int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma,
const struct svc_rdma_pcl *write_pcl,
const struct svc_rdma_pcl *reply_pcl,
@@ -306,6 +316,13 @@ extern void svc_rdma_send_error_msg(struct svcxprt_rdma *rdma,
struct svc_rdma_recv_ctxt *rctxt,
int status);
extern void svc_rdma_wake_send_waiters(struct svcxprt_rdma *rdma, int avail);
+extern int svc_rdma_sq_wait(struct svcxprt_rdma *rdma,
+ const struct rpc_rdma_cid *cid, int sqecount);
+extern int svc_rdma_post_send_err(struct svcxprt_rdma *rdma,
+ const struct rpc_rdma_cid *cid,
+ const struct ib_send_wr *bad_wr,
+ const struct ib_send_wr *first_wr,
+ int sqecount, int ret);
extern int svc_rdma_sendto(struct svc_rqst *);
extern int svc_rdma_result_payload(struct svc_rqst *rqstp, unsigned int offset,
unsigned int length);
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 152597750f55..b639a6fafcbc 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -290,7 +290,7 @@ xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen)
/**
* xdr_set_scratch_folio - Attach a scratch buffer for decoding data
* @xdr: pointer to xdr_stream struct
- * @page: an anonymous folio
+ * @folio: an anonymous folio
*
* See xdr_set_scratch_buffer().
*/
@@ -330,7 +330,7 @@ static inline void xdr_commit_encode(struct xdr_stream *xdr)
* xdr_stream_remaining - Return the number of bytes remaining in the stream
* @xdr: pointer to struct xdr_stream
*
- * Return value:
+ * Returns:
* Number of bytes remaining in @xdr before xdr->end
*/
static inline size_t
@@ -350,7 +350,7 @@ ssize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor,
* xdr_align_size - Calculate padded size of an object
* @n: Size of an object being XDR encoded (in bytes)
*
- * Return value:
+ * Returns:
* Size (in bytes) of the object including xdr padding
*/
static inline size_t
@@ -368,7 +368,7 @@ xdr_align_size(size_t n)
* This implementation avoids the need for conditional
* branches or modulo division.
*
- * Return value:
+ * Returns:
* Size (in bytes) of the needed XDR pad
*/
static inline size_t xdr_pad_size(size_t n)
@@ -380,7 +380,7 @@ static inline size_t xdr_pad_size(size_t n)
* xdr_stream_encode_item_present - Encode a "present" list item
* @xdr: pointer to xdr_stream
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -399,7 +399,7 @@ static inline ssize_t xdr_stream_encode_item_present(struct xdr_stream *xdr)
* xdr_stream_encode_item_absent - Encode a "not present" list item
* @xdr: pointer to xdr_stream
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -419,7 +419,7 @@ static inline int xdr_stream_encode_item_absent(struct xdr_stream *xdr)
* @p: address in a buffer into which to encode
* @n: boolean value to encode
*
- * Return value:
+ * Returns:
* Address of item following the encoded boolean
*/
static inline __be32 *xdr_encode_bool(__be32 *p, u32 n)
@@ -433,7 +433,7 @@ static inline __be32 *xdr_encode_bool(__be32 *p, u32 n)
* @xdr: pointer to xdr_stream
* @n: boolean value to encode
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -453,7 +453,7 @@ static inline int xdr_stream_encode_bool(struct xdr_stream *xdr, __u32 n)
* @xdr: pointer to xdr_stream
* @n: integer to encode
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -474,7 +474,7 @@ xdr_stream_encode_u32(struct xdr_stream *xdr, __u32 n)
* @xdr: pointer to xdr_stream
* @n: integer to encode
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -495,7 +495,7 @@ xdr_stream_encode_be32(struct xdr_stream *xdr, __be32 n)
* @xdr: pointer to xdr_stream
* @n: 64-bit integer to encode
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -517,7 +517,7 @@ xdr_stream_encode_u64(struct xdr_stream *xdr, __u64 n)
* @ptr: pointer to void pointer
* @len: size of object
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -542,7 +542,7 @@ xdr_stream_encode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t len)
* @ptr: pointer to opaque data object
* @len: size of object pointed to by @ptr
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -563,7 +563,7 @@ xdr_stream_encode_opaque_fixed(struct xdr_stream *xdr, const void *ptr, size_t l
* @ptr: pointer to opaque data object
* @len: size of object pointed to by @ptr
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -585,7 +585,7 @@ xdr_stream_encode_opaque(struct xdr_stream *xdr, const void *ptr, size_t len)
* @array: array of integers
* @array_size: number of elements in @array
*
- * Return values:
+ * Returns:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
@@ -608,7 +608,7 @@ xdr_stream_encode_uint32_array(struct xdr_stream *xdr,
* xdr_item_is_absent - symbolically handle XDR discriminators
* @p: pointer to undecoded discriminator
*
- * Return values:
+ * Returns:
* %true if the following XDR item is absent
* %false if the following XDR item is present
*/
@@ -621,7 +621,7 @@ static inline bool xdr_item_is_absent(const __be32 *p)
* xdr_item_is_present - symbolically handle XDR discriminators
* @p: pointer to undecoded discriminator
*
- * Return values:
+ * Returns:
* %true if the following XDR item is present
* %false if the following XDR item is absent
*/
@@ -635,7 +635,7 @@ static inline bool xdr_item_is_present(const __be32 *p)
* @xdr: pointer to xdr_stream
* @ptr: pointer to a u32 in which to store the result
*
- * Return values:
+ * Returns:
* %0 on success
* %-EBADMSG on XDR buffer overflow
*/
@@ -656,7 +656,7 @@ xdr_stream_decode_bool(struct xdr_stream *xdr, __u32 *ptr)
* @xdr: pointer to xdr_stream
* @ptr: location to store integer
*
- * Return values:
+ * Returns:
* %0 on success
* %-EBADMSG on XDR buffer overflow
*/
@@ -677,7 +677,7 @@ xdr_stream_decode_u32(struct xdr_stream *xdr, __u32 *ptr)
* @xdr: pointer to xdr_stream
* @ptr: location to store integer
*
- * Return values:
+ * Returns:
* %0 on success
* %-EBADMSG on XDR buffer overflow
*/
@@ -698,7 +698,7 @@ xdr_stream_decode_be32(struct xdr_stream *xdr, __be32 *ptr)
* @xdr: pointer to xdr_stream
* @ptr: location to store 64-bit integer
*
- * Return values:
+ * Returns:
* %0 on success
* %-EBADMSG on XDR buffer overflow
*/
@@ -720,7 +720,7 @@ xdr_stream_decode_u64(struct xdr_stream *xdr, __u64 *ptr)
* @ptr: location to store data
* @len: size of buffer pointed to by @ptr
*
- * Return values:
+ * Returns:
* %0 on success
* %-EBADMSG on XDR buffer overflow
*/
@@ -746,7 +746,7 @@ xdr_stream_decode_opaque_fixed(struct xdr_stream *xdr, void *ptr, size_t len)
* on @xdr. It is therefore expected that the object it points to should
* be processed immediately.
*
- * Return values:
+ * Returns:
* On success, returns size of object stored in *@ptr
* %-EBADMSG on XDR buffer overflow
* %-EMSGSIZE if the size of the object would exceed @maxlen
@@ -777,7 +777,7 @@ xdr_stream_decode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t maxle
* @array: location to store the integer array or NULL
* @array_size: number of elements to store
*
- * Return values:
+ * Returns:
* On success, returns number of elements stored in @array
* %-EBADMSG on XDR buffer overflow
* %-EMSGSIZE if the size of the array exceeds @array_size
diff --git a/include/linux/sunrpc/xdrgen/nlm4.h b/include/linux/sunrpc/xdrgen/nlm4.h
new file mode 100644
index 000000000000..e95e8f105624
--- /dev/null
+++ b/include/linux/sunrpc/xdrgen/nlm4.h
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Generated by xdrgen. Manual edits will be lost. */
+/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x */
+/* XDR specification modification time: Thu Dec 25 13:10:19 2025 */
+
+#ifndef _LINUX_XDRGEN_NLM4_DEF_H
+#define _LINUX_XDRGEN_NLM4_DEF_H
+
+#include <linux/types.h>
+#include <linux/sunrpc/xdrgen/_defs.h>
+
+enum { LM_MAXSTRLEN = 1024 };
+
+enum { LM_MAXNAMELEN = 1025 };
+
+enum { MAXNETOBJ_SZ = 1024 };
+
+typedef opaque netobj;
+
+enum fsh4_mode {
+ fsm_DN = 0,
+ fsm_DR = 1,
+ fsm_DW = 2,
+ fsm_DRW = 3,
+};
+
+typedef enum fsh4_mode fsh4_mode;
+
+enum fsh4_access {
+ fsa_NONE = 0,
+ fsa_R = 1,
+ fsa_W = 2,
+ fsa_RW = 3,
+};
+
+typedef enum fsh4_access fsh4_access;
+
+enum { SM_MAXSTRLEN = 1024 };
+
+typedef u64 uint64;
+
+typedef s64 int64;
+
+typedef u32 uint32;
+
+typedef s32 int32;
+
+enum nlm4_stats {
+ NLM4_GRANTED = 0,
+ NLM4_DENIED = 1,
+ NLM4_DENIED_NOLOCKS = 2,
+ NLM4_BLOCKED = 3,
+ NLM4_DENIED_GRACE_PERIOD = 4,
+ NLM4_DEADLCK = 5,
+ NLM4_ROFS = 6,
+ NLM4_STALE_FH = 7,
+ NLM4_FBIG = 8,
+ NLM4_FAILED = 9,
+};
+
+typedef __be32 nlm4_stats;
+
+struct nlm4_holder {
+ bool exclusive;
+ int32 svid;
+ netobj oh;
+ uint64 l_offset;
+ uint64 l_len;
+};
+
+struct nlm4_testrply {
+ nlm4_stats stat;
+ union {
+ struct nlm4_holder holder;
+ } u;
+};
+
+struct nlm4_stat {
+ nlm4_stats stat;
+};
+
+struct nlm4_res {
+ netobj cookie;
+ struct nlm4_stat stat;
+};
+
+struct nlm4_testres {
+ netobj cookie;
+ struct nlm4_testrply stat;
+};
+
+struct nlm4_lock {
+ string caller_name;
+ netobj fh;
+ netobj oh;
+ int32 svid;
+ uint64 l_offset;
+ uint64 l_len;
+};
+
+struct nlm4_lockargs {
+ netobj cookie;
+ bool block;
+ bool exclusive;
+ struct nlm4_lock alock;
+ bool reclaim;
+ int32 state;
+};
+
+struct nlm4_cancargs {
+ netobj cookie;
+ bool block;
+ bool exclusive;
+ struct nlm4_lock alock;
+};
+
+struct nlm4_testargs {
+ netobj cookie;
+ bool exclusive;
+ struct nlm4_lock alock;
+};
+
+struct nlm4_unlockargs {
+ netobj cookie;
+ struct nlm4_lock alock;
+};
+
+struct nlm4_share {
+ string caller_name;
+ netobj fh;
+ netobj oh;
+ fsh4_mode mode;
+ fsh4_access access;
+};
+
+struct nlm4_shareargs {
+ netobj cookie;
+ struct nlm4_share share;
+ bool reclaim;
+};
+
+struct nlm4_shareres {
+ netobj cookie;
+ nlm4_stats stat;
+ int32 sequence;
+};
+
+struct nlm4_notify {
+ string name;
+ int32 state;
+};
+
+enum { SM_PRIV_SIZE = 16 };
+
+struct nlm4_notifyargs {
+ struct nlm4_notify notify;
+ u8 private[SM_PRIV_SIZE];
+};
+
+enum {
+ NLMPROC4_NULL = 0,
+ NLMPROC4_TEST = 1,
+ NLMPROC4_LOCK = 2,
+ NLMPROC4_CANCEL = 3,
+ NLMPROC4_UNLOCK = 4,
+ NLMPROC4_GRANTED = 5,
+ NLMPROC4_TEST_MSG = 6,
+ NLMPROC4_LOCK_MSG = 7,
+ NLMPROC4_CANCEL_MSG = 8,
+ NLMPROC4_UNLOCK_MSG = 9,
+ NLMPROC4_GRANTED_MSG = 10,
+ NLMPROC4_TEST_RES = 11,
+ NLMPROC4_LOCK_RES = 12,
+ NLMPROC4_CANCEL_RES = 13,
+ NLMPROC4_UNLOCK_RES = 14,
+ NLMPROC4_GRANTED_RES = 15,
+ NLMPROC4_SM_NOTIFY = 16,
+ NLMPROC4_SHARE = 20,
+ NLMPROC4_UNSHARE = 21,
+ NLMPROC4_NM_LOCK = 22,
+ NLMPROC4_FREE_ALL = 23,
+};
+
+#ifndef NLM4_PROG
+#define NLM4_PROG (100021)
+#endif
+
+#define NLM4_netobj_sz (XDR_unsigned_int + XDR_QUADLEN(MAXNETOBJ_SZ))
+#define NLM4_fsh4_mode_sz (XDR_int)
+#define NLM4_fsh4_access_sz (XDR_int)
+#define NLM4_uint64_sz \
+ (XDR_unsigned_hyper)
+#define NLM4_int64_sz \
+ (XDR_hyper)
+#define NLM4_uint32_sz \
+ (XDR_unsigned_long)
+#define NLM4_int32_sz \
+ (XDR_long)
+#define NLM4_nlm4_stats_sz (XDR_int)
+#define NLM4_nlm4_holder_sz \
+ (XDR_bool + NLM4_int32_sz + NLM4_netobj_sz + NLM4_uint64_sz + NLM4_uint64_sz)
+#define NLM4_nlm4_testrply_sz \
+ (NLM4_nlm4_stats_sz + NLM4_nlm4_holder_sz)
+#define NLM4_nlm4_stat_sz \
+ (NLM4_nlm4_stats_sz)
+#define NLM4_nlm4_res_sz \
+ (NLM4_netobj_sz + NLM4_nlm4_stat_sz)
+#define NLM4_nlm4_testres_sz \
+ (NLM4_netobj_sz + NLM4_nlm4_testrply_sz)
+#define NLM4_nlm4_lock_sz \
+ (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM4_netobj_sz + NLM4_netobj_sz + NLM4_int32_sz + NLM4_uint64_sz + NLM4_uint64_sz)
+#define NLM4_nlm4_lockargs_sz \
+ (NLM4_netobj_sz + XDR_bool + XDR_bool + NLM4_nlm4_lock_sz + XDR_bool + NLM4_int32_sz)
+#define NLM4_nlm4_cancargs_sz \
+ (NLM4_netobj_sz + XDR_bool + XDR_bool + NLM4_nlm4_lock_sz)
+#define NLM4_nlm4_testargs_sz \
+ (NLM4_netobj_sz + XDR_bool + NLM4_nlm4_lock_sz)
+#define NLM4_nlm4_unlockargs_sz \
+ (NLM4_netobj_sz + NLM4_nlm4_lock_sz)
+#define NLM4_nlm4_share_sz \
+ (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM4_netobj_sz + NLM4_netobj_sz + NLM4_fsh4_mode_sz + NLM4_fsh4_access_sz)
+#define NLM4_nlm4_shareargs_sz \
+ (NLM4_netobj_sz + NLM4_nlm4_share_sz + XDR_bool)
+#define NLM4_nlm4_shareres_sz \
+ (NLM4_netobj_sz + NLM4_nlm4_stats_sz + NLM4_int32_sz)
+#define NLM4_nlm4_notify_sz \
+ (XDR_unsigned_int + XDR_QUADLEN(LM_MAXNAMELEN) + NLM4_int32_sz)
+#define NLM4_nlm4_notifyargs_sz \
+ (NLM4_nlm4_notify_sz + XDR_QUADLEN(SM_PRIV_SIZE))
+#define NLM4_MAX_ARGS_SZ \
+ (NLM4_nlm4_lockargs_sz)
+
+#endif /* _LINUX_XDRGEN_NLM4_DEF_H */