linux-nfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] xdrgen - machine-generated XDR functions
@ 2024-08-20 14:45 cel
  2024-08-20 14:45 ` [RFC PATCH 1/2] tools: Add xdrgen cel
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: cel @ 2024-08-20 14:45 UTC (permalink / raw)
  To: linux-nfs; +Cc: Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

For a description of this work-in-progress, see the new README file.
For unfinished work items, ditto.

The first patch adds the tool.

The second patch is an nfs4_1.x file that I created to confirm that
the tool indeed works for Jeff's purposes. Actual generated header
and source are in fs/nfsd/nfs4xdr_gen.? .

Feel free to ignore 2/2. It is included here only to demonstrate the
tool's new "pragma" directives that I think can avoid the need to
hand-edit generated source, as discussed in an earlier thread. Grep
the new README or nfs4_1.x for "pragma".

These patches apply to v6.11-rc4, but can be rebased on nfsd-next if
they are found merge-worthy. See also:

https://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git/log/?h=lkxdrgen

For review, note that the numerous .j2 files are all tiny and can be
skimmed in favor of the .py files, where the tool's logic is.

All Python-related advice and guidance is gratefully accepted.

--

Chuck Lever (2):
  tools: Add xdrgen
  NFSD: Create an initial nfs4_1.x file

 fs/nfsd/nfs4_1.x                              | 164 +++++++
 fs/nfsd/nfs4xdr_gen.c                         | 236 ++++++++++
 fs/nfsd/nfs4xdr_gen.h                         | 113 +++++
 include/linux/sunrpc/xdrgen-builtins.h        | 253 ++++++++++
 tools/net/sunrpc/xdrgen/.gitignore            |   2 +
 tools/net/sunrpc/xdrgen/README                | 231 +++++++++
 tools/net/sunrpc/xdrgen/__init__.py           |   0
 tools/net/sunrpc/xdrgen/common.py             |  21 +
 .../net/sunrpc/xdrgen/generators/__init__.py  |   0
 .../sunrpc/xdrgen/generators/boilerplate.py   |  66 +++
 .../net/sunrpc/xdrgen/generators/constant.py  |  38 ++
 tools/net/sunrpc/xdrgen/generators/enum.py    |  67 +++
 tools/net/sunrpc/xdrgen/generators/program.py |  96 ++++
 tools/net/sunrpc/xdrgen/generators/struct.py  | 435 +++++++++++++++++
 tools/net/sunrpc/xdrgen/generators/typedef.py | 282 +++++++++++
 tools/net/sunrpc/xdrgen/generators/union.py   | 297 ++++++++++++
 tools/net/sunrpc/xdrgen/subcmds/__init__.py   |   0
 tools/net/sunrpc/xdrgen/subcmds/header.py     |  62 +++
 tools/net/sunrpc/xdrgen/subcmds/lint.py       |  28 ++
 tools/net/sunrpc/xdrgen/subcmds/source.py     |  82 ++++
 .../templates/C/boilerplate/header_bottom.j2  |   3 +
 .../templates/C/boilerplate/header_top.j2     |  11 +
 .../templates/C/boilerplate/source_top.j2     |   5 +
 .../templates/C/constants/declaration.j2      |   5 +
 .../templates/C/enum/declaration/close.j2     |   7 +
 .../C/enum/declaration/enumerator.j2          |   2 +
 .../templates/C/enum/declaration/open.j2      |   3 +
 .../xdrgen/templates/C/enum/decoder/enum.j2   |  19 +
 .../xdrgen/templates/C/enum/encoder/enum.j2   |  14 +
 .../C/program/declaration/argument.j2         |   2 +
 .../templates/C/program/declaration/result.j2 |   2 +
 .../templates/C/program/decoder/argument.j2   |  21 +
 .../templates/C/program/encoder/result.j2     |  21 +
 .../templates/C/struct/declaration/basic.j2   |   5 +
 .../templates/C/struct/declaration/close.j2   |   7 +
 .../struct/declaration/fixed_length_array.j2  |   5 +
 .../struct/declaration/fixed_length_opaque.j2 |   5 +
 .../templates/C/struct/declaration/open.j2    |   6 +
 .../C/struct/declaration/optional_data.j2     |   5 +
 .../C/struct/declaration/pointer-open.j2      |   6 +
 .../declaration/variable_length_array.j2      |   8 +
 .../declaration/variable_length_opaque.j2     |   5 +
 .../declaration/variable_length_string.j2     |   5 +
 .../templates/C/struct/decoder/basic.j2       |   6 +
 .../templates/C/struct/decoder/close.j2       |   3 +
 .../C/struct/decoder/fixed_length_array.j2    |   8 +
 .../C/struct/decoder/fixed_length_opaque.j2   |   6 +
 .../xdrgen/templates/C/struct/decoder/open.j2 |  12 +
 .../C/struct/decoder/optional_data.j2         |   6 +
 .../C/struct/decoder/pointer-open.j2          |  22 +
 .../C/struct/decoder/variable_length_array.j2 |  13 +
 .../struct/decoder/variable_length_opaque.j2  |   6 +
 .../struct/decoder/variable_length_string.j2  |   6 +
 .../templates/C/struct/encoder/basic.j2       |  10 +
 .../templates/C/struct/encoder/close.j2       |   3 +
 .../C/struct/encoder/fixed_length_array.j2    |  12 +
 .../C/struct/encoder/fixed_length_opaque.j2   |   6 +
 .../xdrgen/templates/C/struct/encoder/open.j2 |  12 +
 .../C/struct/encoder/optional_data.j2         |   6 +
 .../C/struct/encoder/pointer-open.j2          |  20 +
 .../C/struct/encoder/variable_length_array.j2 |  15 +
 .../struct/encoder/variable_length_opaque.j2  |   8 +
 .../struct/encoder/variable_length_string.j2  |   8 +
 .../templates/C/typedef/declaration/basic.j2  |  11 +
 .../typedef/declaration/fixed_length_array.j2 |  11 +
 .../declaration/fixed_length_opaque.j2        |  11 +
 .../declaration/variable_length_array.j2      |  14 +
 .../declaration/variable_length_opaque.j2     |  11 +
 .../declaration/variable_length_string.j2     |  11 +
 .../templates/C/typedef/decoder/basic.j2      |  17 +
 .../C/typedef/decoder/fixed_length_array.j2   |  25 +
 .../C/typedef/decoder/fixed_length_opaque.j2  |  17 +
 .../typedef/decoder/variable_length_array.j2  |  26 ++
 .../typedef/decoder/variable_length_opaque.j2 |  17 +
 .../typedef/decoder/variable_length_string.j2 |  17 +
 .../templates/C/typedef/encoder/basic.j2      |  21 +
 .../C/typedef/encoder/fixed_length_array.j2   |  25 +
 .../C/typedef/encoder/fixed_length_opaque.j2  |  17 +
 .../typedef/encoder/variable_length_array.j2  |  30 ++
 .../typedef/encoder/variable_length_opaque.j2 |  17 +
 .../typedef/encoder/variable_length_string.j2 |  17 +
 .../C/union/declaration/case_spec.j2          |   2 +
 .../templates/C/union/declaration/close.j2    |   8 +
 .../C/union/declaration/default_spec.j2       |   2 +
 .../templates/C/union/declaration/open.j2     |   6 +
 .../C/union/declaration/switch_spec.j2        |   3 +
 .../xdrgen/templates/C/union/decoder/basic.j2 |   6 +
 .../xdrgen/templates/C/union/decoder/break.j2 |   2 +
 .../templates/C/union/decoder/case_spec.j2    |   2 +
 .../xdrgen/templates/C/union/decoder/close.j2 |   4 +
 .../templates/C/union/decoder/default_spec.j2 |   2 +
 .../xdrgen/templates/C/union/decoder/open.j2  |  12 +
 .../C/union/decoder/optional_data.j2          |   6 +
 .../templates/C/union/decoder/switch_spec.j2  |   7 +
 .../C/union/decoder/variable_length_array.j2  |  13 +
 .../C/union/decoder/variable_length_opaque.j2 |   6 +
 .../C/union/decoder/variable_length_string.j2 |   6 +
 .../xdrgen/templates/C/union/decoder/void.j2  |   3 +
 .../xdrgen/templates/C/union/encoder/basic.j2 |  10 +
 .../xdrgen/templates/C/union/encoder/break.j2 |   2 +
 .../templates/C/union/encoder/case_spec.j2    |   2 +
 .../xdrgen/templates/C/union/encoder/close.j2 |   4 +
 .../templates/C/union/encoder/default_spec.j2 |   2 +
 .../xdrgen/templates/C/union/encoder/open.j2  |  12 +
 .../templates/C/union/encoder/switch_spec.j2  |   7 +
 .../xdrgen/templates/C/union/encoder/void.j2  |   3 +
 tools/net/sunrpc/xdrgen/tests/test.x          |  36 ++
 tools/net/sunrpc/xdrgen/xdr                   |   1 +
 tools/net/sunrpc/xdrgen/xdr.ebnf              | 117 +++++
 tools/net/sunrpc/xdrgen/xdr_ast.py            | 437 ++++++++++++++++++
 tools/net/sunrpc/xdrgen/xdr_parse.py          |  20 +
 tools/net/sunrpc/xdrgen/xdrgen                |  95 ++++
 112 files changed, 3986 insertions(+)
 create mode 100644 fs/nfsd/nfs4_1.x
 create mode 100644 fs/nfsd/nfs4xdr_gen.c
 create mode 100644 fs/nfsd/nfs4xdr_gen.h
 create mode 100644 include/linux/sunrpc/xdrgen-builtins.h
 create mode 100644 tools/net/sunrpc/xdrgen/.gitignore
 create mode 100644 tools/net/sunrpc/xdrgen/README
 create mode 100644 tools/net/sunrpc/xdrgen/__init__.py
 create mode 100644 tools/net/sunrpc/xdrgen/common.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/__init__.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/boilerplate.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/constant.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/enum.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/program.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/struct.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/typedef.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/union.py
 create mode 100644 tools/net/sunrpc/xdrgen/subcmds/__init__.py
 create mode 100755 tools/net/sunrpc/xdrgen/subcmds/header.py
 create mode 100755 tools/net/sunrpc/xdrgen/subcmds/lint.py
 create mode 100755 tools/net/sunrpc/xdrgen/subcmds/source.py
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_bottom.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_top.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/source_top.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/constants/declaration.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enumerator.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/pointer-open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/pointer-open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/pointer-open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/case_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/default_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/switch_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2
 create mode 100644 tools/net/sunrpc/xdrgen/tests/test.x
 create mode 120000 tools/net/sunrpc/xdrgen/xdr
 create mode 100644 tools/net/sunrpc/xdrgen/xdr.ebnf
 create mode 100644 tools/net/sunrpc/xdrgen/xdr_ast.py
 create mode 100644 tools/net/sunrpc/xdrgen/xdr_parse.py
 create mode 100755 tools/net/sunrpc/xdrgen/xdrgen

-- 
2.45.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 1/2] tools: Add xdrgen
  2024-08-20 14:45 [RFC PATCH 0/2] xdrgen - machine-generated XDR functions cel
@ 2024-08-20 14:45 ` cel
  2024-08-20 14:46 ` [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file cel
  2024-08-21 14:03 ` [RFC PATCH 0/2] xdrgen - machine-generated XDR functions Jeff Layton
  2 siblings, 0 replies; 16+ messages in thread
From: cel @ 2024-08-20 14:45 UTC (permalink / raw)
  To: linux-nfs; +Cc: Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

Add a Python-based tool for translating XDR specifications into XDR
encoder and decoder functions written in the Linux kernel's C coding
style. The generator attempts to match the usual C coding style of
the Linux kernel's sunrpc/ layer.

This approach is similar to the netlink code generator in
tools/net/ynl .

The maintainability benefits of machine-generated XDR code include:

- Stronger type checking
- Reduces the number of bugs introduced by human error
- Makes the XDR code easier to audit and analyze
- Enables rapid prototyping of new RPC-based protocols
- Hardens the layering between protocol logic and marshaling
- Makes it easier to add observability on demand
- Unit tests might be built for both the tool and (automatically)
  for the generated code

In addition, converting the XDR layer to use memory-safe languages
such as Rust will be easier if much of the code can be converted
automatically.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 include/linux/sunrpc/xdrgen-builtins.h        | 253 ++++++++++
 tools/net/sunrpc/xdrgen/.gitignore            |   2 +
 tools/net/sunrpc/xdrgen/README                | 231 +++++++++
 tools/net/sunrpc/xdrgen/__init__.py           |   0
 tools/net/sunrpc/xdrgen/common.py             |  21 +
 .../net/sunrpc/xdrgen/generators/__init__.py  |   0
 .../sunrpc/xdrgen/generators/boilerplate.py   |  66 +++
 .../net/sunrpc/xdrgen/generators/constant.py  |  38 ++
 tools/net/sunrpc/xdrgen/generators/enum.py    |  67 +++
 tools/net/sunrpc/xdrgen/generators/program.py |  96 ++++
 tools/net/sunrpc/xdrgen/generators/struct.py  | 435 +++++++++++++++++
 tools/net/sunrpc/xdrgen/generators/typedef.py | 282 +++++++++++
 tools/net/sunrpc/xdrgen/generators/union.py   | 297 ++++++++++++
 tools/net/sunrpc/xdrgen/subcmds/__init__.py   |   0
 tools/net/sunrpc/xdrgen/subcmds/header.py     |  62 +++
 tools/net/sunrpc/xdrgen/subcmds/lint.py       |  28 ++
 tools/net/sunrpc/xdrgen/subcmds/source.py     |  82 ++++
 .../templates/C/boilerplate/header_bottom.j2  |   3 +
 .../templates/C/boilerplate/header_top.j2     |  11 +
 .../templates/C/boilerplate/source_top.j2     |   5 +
 .../templates/C/constants/declaration.j2      |   5 +
 .../templates/C/enum/declaration/close.j2     |   7 +
 .../C/enum/declaration/enumerator.j2          |   2 +
 .../templates/C/enum/declaration/open.j2      |   3 +
 .../xdrgen/templates/C/enum/decoder/enum.j2   |  19 +
 .../xdrgen/templates/C/enum/encoder/enum.j2   |  14 +
 .../C/program/declaration/argument.j2         |   2 +
 .../templates/C/program/declaration/result.j2 |   2 +
 .../templates/C/program/decoder/argument.j2   |  21 +
 .../templates/C/program/encoder/result.j2     |  21 +
 .../templates/C/struct/declaration/basic.j2   |   5 +
 .../templates/C/struct/declaration/close.j2   |   7 +
 .../struct/declaration/fixed_length_array.j2  |   5 +
 .../struct/declaration/fixed_length_opaque.j2 |   5 +
 .../templates/C/struct/declaration/open.j2    |   6 +
 .../C/struct/declaration/optional_data.j2     |   5 +
 .../C/struct/declaration/pointer-open.j2      |   6 +
 .../declaration/variable_length_array.j2      |   8 +
 .../declaration/variable_length_opaque.j2     |   5 +
 .../declaration/variable_length_string.j2     |   5 +
 .../templates/C/struct/decoder/basic.j2       |   6 +
 .../templates/C/struct/decoder/close.j2       |   3 +
 .../C/struct/decoder/fixed_length_array.j2    |   8 +
 .../C/struct/decoder/fixed_length_opaque.j2   |   6 +
 .../xdrgen/templates/C/struct/decoder/open.j2 |  12 +
 .../C/struct/decoder/optional_data.j2         |   6 +
 .../C/struct/decoder/pointer-open.j2          |  22 +
 .../C/struct/decoder/variable_length_array.j2 |  13 +
 .../struct/decoder/variable_length_opaque.j2  |   6 +
 .../struct/decoder/variable_length_string.j2  |   6 +
 .../templates/C/struct/encoder/basic.j2       |  10 +
 .../templates/C/struct/encoder/close.j2       |   3 +
 .../C/struct/encoder/fixed_length_array.j2    |  12 +
 .../C/struct/encoder/fixed_length_opaque.j2   |   6 +
 .../xdrgen/templates/C/struct/encoder/open.j2 |  12 +
 .../C/struct/encoder/optional_data.j2         |   6 +
 .../C/struct/encoder/pointer-open.j2          |  20 +
 .../C/struct/encoder/variable_length_array.j2 |  15 +
 .../struct/encoder/variable_length_opaque.j2  |   8 +
 .../struct/encoder/variable_length_string.j2  |   8 +
 .../templates/C/typedef/declaration/basic.j2  |  11 +
 .../typedef/declaration/fixed_length_array.j2 |  11 +
 .../declaration/fixed_length_opaque.j2        |  11 +
 .../declaration/variable_length_array.j2      |  14 +
 .../declaration/variable_length_opaque.j2     |  11 +
 .../declaration/variable_length_string.j2     |  11 +
 .../templates/C/typedef/decoder/basic.j2      |  17 +
 .../C/typedef/decoder/fixed_length_array.j2   |  25 +
 .../C/typedef/decoder/fixed_length_opaque.j2  |  17 +
 .../typedef/decoder/variable_length_array.j2  |  26 ++
 .../typedef/decoder/variable_length_opaque.j2 |  17 +
 .../typedef/decoder/variable_length_string.j2 |  17 +
 .../templates/C/typedef/encoder/basic.j2      |  21 +
 .../C/typedef/encoder/fixed_length_array.j2   |  25 +
 .../C/typedef/encoder/fixed_length_opaque.j2  |  17 +
 .../typedef/encoder/variable_length_array.j2  |  30 ++
 .../typedef/encoder/variable_length_opaque.j2 |  17 +
 .../typedef/encoder/variable_length_string.j2 |  17 +
 .../C/union/declaration/case_spec.j2          |   2 +
 .../templates/C/union/declaration/close.j2    |   8 +
 .../C/union/declaration/default_spec.j2       |   2 +
 .../templates/C/union/declaration/open.j2     |   6 +
 .../C/union/declaration/switch_spec.j2        |   3 +
 .../xdrgen/templates/C/union/decoder/basic.j2 |   6 +
 .../xdrgen/templates/C/union/decoder/break.j2 |   2 +
 .../templates/C/union/decoder/case_spec.j2    |   2 +
 .../xdrgen/templates/C/union/decoder/close.j2 |   4 +
 .../templates/C/union/decoder/default_spec.j2 |   2 +
 .../xdrgen/templates/C/union/decoder/open.j2  |  12 +
 .../C/union/decoder/optional_data.j2          |   6 +
 .../templates/C/union/decoder/switch_spec.j2  |   7 +
 .../C/union/decoder/variable_length_array.j2  |  13 +
 .../C/union/decoder/variable_length_opaque.j2 |   6 +
 .../C/union/decoder/variable_length_string.j2 |   6 +
 .../xdrgen/templates/C/union/decoder/void.j2  |   3 +
 .../xdrgen/templates/C/union/encoder/basic.j2 |  10 +
 .../xdrgen/templates/C/union/encoder/break.j2 |   2 +
 .../templates/C/union/encoder/case_spec.j2    |   2 +
 .../xdrgen/templates/C/union/encoder/close.j2 |   4 +
 .../templates/C/union/encoder/default_spec.j2 |   2 +
 .../xdrgen/templates/C/union/encoder/open.j2  |  12 +
 .../templates/C/union/encoder/switch_spec.j2  |   7 +
 .../xdrgen/templates/C/union/encoder/void.j2  |   3 +
 tools/net/sunrpc/xdrgen/tests/test.x          |  36 ++
 tools/net/sunrpc/xdrgen/xdr                   |   1 +
 tools/net/sunrpc/xdrgen/xdr.ebnf              | 117 +++++
 tools/net/sunrpc/xdrgen/xdr_ast.py            | 437 ++++++++++++++++++
 tools/net/sunrpc/xdrgen/xdr_parse.py          |  20 +
 tools/net/sunrpc/xdrgen/xdrgen                |  95 ++++
 109 files changed, 3473 insertions(+)
 create mode 100644 include/linux/sunrpc/xdrgen-builtins.h
 create mode 100644 tools/net/sunrpc/xdrgen/.gitignore
 create mode 100644 tools/net/sunrpc/xdrgen/README
 create mode 100644 tools/net/sunrpc/xdrgen/__init__.py
 create mode 100644 tools/net/sunrpc/xdrgen/common.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/__init__.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/boilerplate.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/constant.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/enum.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/program.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/struct.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/typedef.py
 create mode 100644 tools/net/sunrpc/xdrgen/generators/union.py
 create mode 100644 tools/net/sunrpc/xdrgen/subcmds/__init__.py
 create mode 100755 tools/net/sunrpc/xdrgen/subcmds/header.py
 create mode 100755 tools/net/sunrpc/xdrgen/subcmds/lint.py
 create mode 100755 tools/net/sunrpc/xdrgen/subcmds/source.py
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_bottom.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_top.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/source_top.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/constants/declaration.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enumerator.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/pointer-open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/pointer-open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/pointer-open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/case_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/default_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/switch_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2
 create mode 100644 tools/net/sunrpc/xdrgen/tests/test.x
 create mode 120000 tools/net/sunrpc/xdrgen/xdr
 create mode 100644 tools/net/sunrpc/xdrgen/xdr.ebnf
 create mode 100644 tools/net/sunrpc/xdrgen/xdr_ast.py
 create mode 100644 tools/net/sunrpc/xdrgen/xdr_parse.py
 create mode 100755 tools/net/sunrpc/xdrgen/xdrgen

diff --git a/include/linux/sunrpc/xdrgen-builtins.h b/include/linux/sunrpc/xdrgen-builtins.h
new file mode 100644
index 000000000000..01daef4b66b7
--- /dev/null
+++ b/include/linux/sunrpc/xdrgen-builtins.h
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * This header defines the XDR data type primitives, specified in
+ * Section 4 of RFC 4506, that are used by RPC programs implemented
+ * in the Linux kernel.
+ */
+
+#ifndef _SUNRPC_XDRGEN_BUILTINS_H_
+#define _SUNRPC_XDRGEN_BUILTINS_H_
+
+#include <linux/sunrpc/xdr.h>
+
+static inline bool
+xdrgen_decode_void(struct xdr_stream *xdr)
+{
+	return true;
+}
+
+static inline bool
+xdrgen_encode_void(struct xdr_stream *xdr)
+{
+	return true;
+}
+
+static inline bool
+xdrgen_decode_bool(struct xdr_stream *xdr, bool *ptr)
+{
+	__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*ptr = (*p != xdr_zero);
+	return true;
+}
+
+static inline bool
+xdrgen_encode_bool(struct xdr_stream *xdr, bool val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*p = val ? xdr_one : xdr_zero;
+	return true;
+}
+
+static inline bool
+xdrgen_decode_int(struct xdr_stream *xdr, s32 *ptr)
+{
+	__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*ptr = be32_to_cpup(p);
+	return true;
+}
+
+static inline bool
+xdrgen_encode_int(struct xdr_stream *xdr, s32 val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*p = cpu_to_be32(val);
+	return true;
+}
+
+static inline bool
+xdrgen_decode_unsigned_int(struct xdr_stream *xdr, u32 *ptr)
+{
+	__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*ptr = be32_to_cpup(p);
+	return true;
+}
+
+static inline bool
+xdrgen_encode_unsigned_int(struct xdr_stream *xdr, u32 val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*p = cpu_to_be32(val);
+	return true;
+}
+
+static inline bool
+xdrgen_decode_long(struct xdr_stream *xdr, s32 *ptr)
+{
+	__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*ptr = be32_to_cpup(p);
+	return true;
+}
+
+static inline bool
+xdrgen_encode_long(struct xdr_stream *xdr, s32 val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*p = cpu_to_be32(val);
+	return true;
+}
+
+static inline bool
+xdrgen_decode_unsigned_long(struct xdr_stream *xdr, u32 *ptr)
+{
+	__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*ptr = be32_to_cpup(p);
+	return true;
+}
+
+static inline bool
+xdrgen_encode_unsigned_long(struct xdr_stream *xdr, u32 val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+	if (unlikely(!p))
+		return false;
+	*p = cpu_to_be32(val);
+	return true;
+}
+
+static inline bool
+xdrgen_decode_hyper(struct xdr_stream *xdr, s64 *ptr)
+{
+	__be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
+
+	if (unlikely(!p))
+		return false;
+	*ptr = get_unaligned_be64(p);
+	return true;
+}
+
+static inline bool
+xdrgen_encode_hyper(struct xdr_stream *xdr, s64 val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
+
+	if (unlikely(!p))
+		return false;
+	put_unaligned_be64(val, p);
+	return true;
+}
+
+static inline bool
+xdrgen_decode_unsigned_hyper(struct xdr_stream *xdr, u64 *ptr)
+{
+	__be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
+
+	if (unlikely(!p))
+		return false;
+	*ptr = get_unaligned_be64(p);
+	return true;
+}
+
+static inline bool
+xdrgen_encode_unsigned_hyper(struct xdr_stream *xdr, u64 val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
+
+	if (unlikely(!p))
+		return false;
+	put_unaligned_be64(val, p);
+	return true;
+}
+
+typedef struct {
+	u32 len;
+	unsigned char *data;
+} string;
+
+static inline bool
+xdrgen_decode_string(struct xdr_stream *xdr, string *ptr, u32 maxlen)
+{
+	__be32 *p;
+	u32 len;
+
+	if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT))
+		return false;
+	if (unlikely(maxlen && len > maxlen))
+		return false;
+	if (len != 0) {
+		p = xdr_inline_decode(xdr, len);
+		if (unlikely(!p))
+			return false;
+		ptr->data = (unsigned char *)p;
+	}
+	ptr->len = len;
+	return true;
+}
+
+static inline bool
+xdrgen_encode_string(struct xdr_stream *xdr, string val, u32 maxlen)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
+
+	if (unlikely(!p))
+		return false;
+	xdr_encode_opaque(p, val.data, val.len);
+	return true;
+}
+
+typedef struct {
+	u32 len;
+	u8 *data;
+} opaque;
+
+static inline bool
+xdrgen_decode_opaque(struct xdr_stream *xdr, opaque *ptr, u32 maxlen)
+{
+	__be32 *p;
+	u32 len;
+
+	if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT))
+		return false;
+	if (unlikely(maxlen && len > maxlen))
+		return false;
+	if (len != 0) {
+		p = xdr_inline_decode(xdr, len);
+		if (unlikely(!p))
+			return false;
+		ptr->data = (u8 *)p;
+	}
+	ptr->len = len;
+	return true;
+}
+
+static inline bool
+xdrgen_encode_opaque(struct xdr_stream *xdr, opaque val)
+{
+	__be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
+
+	if (unlikely(!p))
+		return false;
+	xdr_encode_opaque(p, val.data, val.len);
+	return true;
+}
+
+#endif /* _SUNRPC_XDRGEN_BUILTINS_H_ */
diff --git a/tools/net/sunrpc/xdrgen/.gitignore b/tools/net/sunrpc/xdrgen/.gitignore
new file mode 100644
index 000000000000..d7366c2f9be8
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+generators/__pycache__
diff --git a/tools/net/sunrpc/xdrgen/README b/tools/net/sunrpc/xdrgen/README
new file mode 100644
index 000000000000..1b67c1838dae
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/README
@@ -0,0 +1,231 @@
+xdrgen - Linux Kernel XDR code generator
+
+Introduction
+------------
+
+SunRPC programs are typically specified using a language defined by
+RFC 4506. In fact, all IETF-published NFS specifications provide a
+description of the specified protocol using this language.
+
+Since the 1990's, user space consumers of SunRPC have had access to
+a tool that could read such XDR specifications and then generate C
+code that implements the RPC portions of that protocol. This tool is
+called rpcgen.
+
+This RPC-level code is code that handles input directly from the
+network, and thus a high degree of memory safety and sanity checking
+is needed to help ensure proper levels of security. Bugs in this
+code can have significant impact on security and performance.
+
+However, it is code that is repetitive and tedious to write by hand.
+
+The C code generated by rpcgen makes extensive use of the facilities
+of the user space TI-RPC library and libc. Furthermore, the dialect
+of the generated code is very traditional K&R C.
+
+The Linux kernel's implementation of SunRPC-based protocols hand-roll
+their XDR implementation. There are two main reasons for this:
+
+1. libtirpc (and its predecessors) operate only in user space. The
+   kernel's RPC implementation and its API are significantly
+   different than libtirpc.
+
+2. rpcgen-generated code is believed to be less efficient than code
+   that is hand-written.
+
+These days, gcc and its kin are capable of optimizing code better
+than human authors. There are only a few instances where writing
+XDR code by hand will make a measurable performance different.
+
+In addition, the current hand-written code in the Linux kernel is
+difficult to audit and prove that it implements exactly what is in
+the protocol specification.
+
+In order to accrue the benefits of machine-generated XDR code in the
+kernel, a tool is needed that will output C code that works against
+the kernel's SunRPC implementation rather than libtirpc.
+
+Enter xdrgen.
+
+
+Dependencies
+------------
+
+These dependencies are typically packaged by Linux distributions:
+
+- python3
+- python3-lark
+- python3-jinja2
+
+These dependencies are available via PyPi:
+
+- pip install 'lark[interegular]'
+
+
+XDR Specifications
+------------------
+
+XDR specifications for SunRPC-based protocols implemented in the
+Linux kernel are located here:
+
+   Documentation/sunrpc/xdr/
+
+A symlink has been added to the xdrgen directory for easy access to
+those files.
+
+When adding a new protocol implementation to the kernel, the XDR
+specification can be derived by feeding a .txt copy of the RFC to
+the script located in tools/net/sunrpc/extract.sh.
+
+   $ extract.sh < rfc0001.txt > xdr/new2.x
+
+
+Operation
+---------
+
+Once a .x file is available, use xdrgen to generate a C source and
+header file containing an implementation of XDR encoding and
+decoding functions for the specified protocol.
+
+   $ ./xdrgen header xdr/new.x > new2xdr_gen.h
+
+and
+
+   $ ./xdrgen source xdr/new.x > new2xdr_gen.c
+
+The files are ready to use for a server-side protocol implementation,
+or may be used as a guide for implementing these routines by hand.
+
+By default, the only comments added to this code are kdoc comments
+that appear directly in front of the public per-procedure APIs. For
+deeper introspection, specifying the "--annotate" flag will insert
+additional comments in the generated code to help readers match the
+generated code to specific parts of the XDR specification.
+
+Because the generated code is targeted for the Linux kernel, it
+is tagged with a GPLv2-only license.
+
+The xdrgen tool can also provide lexical and syntax checking of
+an XDR specification:
+
+   $ ./xdrgen lint xdr/new.x
+
+
+How It Works
+------------
+
+xdrgen does not use machine learning to generate source code. The
+translation is entirely deterministic.
+
+RFC 4506 Section 6 contains a BNF grammar of the XDR specification
+language. The grammar has been adapted for use by the Python Lark
+module.
+
+The xdr.ebnf file in this directory contains the grammar used to
+parse XDR specifications. xdrgen configures Lark using the grammar
+in xdr.ebnf. Lark parses the target XDR specification using this
+grammar, creating a parse tree.
+
+xdrgen then transforms the parse tree into an abstract syntax tree.
+This tree is passed to a series of code generators.
+
+The generators are implemented as Python classes residing in the
+generators/ directory. Each generator emits code created from Jinja2
+templates stored in the templates/ directory.
+
+The source code is generated in the same order in which they appear
+in the specification to ensure the generated code compiles. This
+conforms with the behavior of rpcgen.
+
+xdrgen assumes that the generated source code is further compiled by
+a compiler that can optimize in a number of ways, including:
+
+ - Unused functions are discarded (ie, not added to the executable)
+
+ - Aggressive function inlining removes unnecessary stack frames
+
+ - Single-arm switch statements are replaced by a single conditional
+   branch
+
+And so on.
+
+
+Pragmas
+-------
+
+Pragma directives specify exceptions to the normal generation of
+encoding and decoding functions. Currently one directive is
+implemented: "public".
+
+Pragma header
+------ ------
+
+  pragma header <string> ;
+
+Provide a name to use for the header file and header guards.
+
+For example:
+
+  pragma header NLM4;
+
+Adds header guards in the generated header file called
+
+  NLM4_XDR_GEN_H
+
+And adds
+
+  #include "nlm4xdr_gen.h"
+
+to the generated C source file.
+
+Pragma public
+------ ------
+
+  pragma public <XDR data item> ;
+
+Normally XDR encoder and decoder functions are "static". In case an
+implementer wants to call these functions from other source code,
+s/he can add a public pragma in the input .x file to indicate a set
+of functions that should get a prototype in the generated header,
+and the function definitions will not be declared static.
+
+For example:
+
+  pragma public nfsstat3;
+
+Adds these prototypes in the generated header:
+
+  bool xdrgen_decode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 *ptr);
+  bool xdrgen_encode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 value);
+
+And, in the generated source code, both of these functions appear
+without the "static __maybe_unused" declaration.
+
+
+Future Work
+-----------
+
+Enable xdrgen to be run in any directory rather than only in
+tools/net/sunrpc/xdrgen.
+
+Add more pragma directives:
+
+  * @exclude -- don't emit procedure argument or result encoders;
+    the specified procedure will be hand-rolled
+  * @pages -- use xdr_read/write_pages() for the specified opaque
+    field
+  * @skip -- do not decode, but rather skip, the specified argument
+    field
+
+Enable something like a #include to dynamically insert the content
+of other specification files
+
+Properly support line-by-line pass-through via the "%" decorator
+
+Build a unit test suite for verifying translation of XDR language
+into C code
+
+Generate kernel Rust code as well as C code
+
+Add a command-line option to insert trace_printk call sites in the
+generated source code, for improved (temporary) observability
diff --git a/tools/net/sunrpc/xdrgen/__init__.py b/tools/net/sunrpc/xdrgen/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/tools/net/sunrpc/xdrgen/common.py b/tools/net/sunrpc/xdrgen/common.py
new file mode 100644
index 000000000000..c493ffbdddb0
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/common.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Utility functions and global variables for xdrgen."""
+
+global annotate
+annotate = False
+
+
+def set_xdr_annotate(set_it):
+    """Set 'annotate' if --annotate was specified on the command line"""
+    global annotate
+
+    annotate = set_it
+
+
+def get_xdr_annotate():
+    """Return True if --annotate was specified on the command line"""
+    global annotate
+
+    return annotate
diff --git a/tools/net/sunrpc/xdrgen/generators/__init__.py b/tools/net/sunrpc/xdrgen/generators/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/tools/net/sunrpc/xdrgen/generators/boilerplate.py b/tools/net/sunrpc/xdrgen/generators/boilerplate.py
new file mode 100644
index 000000000000..8680161dd3a3
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/boilerplate.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate boilerplate items"""
+
+import os.path
+import time
+from jinja2 import Environment, FileSystemLoader
+
+from xdr_ast import _RpcProgram, get_header_name
+
+
+def find_xdr_program_name(ast):
+    """Retrieve the RPC program name from an abstract syntax tree"""
+    raw_name = get_header_name()
+    if raw_name != "none":
+        return raw_name.lower()
+    for definition in ast.definitions:
+        if isinstance(definition.value, _RpcProgram):
+            raw_name = definition.value.name
+            return raw_name.lower().removesuffix("_program").removesuffix("_prog")
+    return "noprog"
+
+
+class SourceGenerator:
+    """Generate source code boilerplate"""
+
+    def __init__(self, language):
+        """Set the output language"""
+        match language:
+            case "C":
+                self.environment = Environment(
+                    loader=FileSystemLoader("templates/C/boilerplate/"),
+                    trim_blocks=True,
+                    lstrip_blocks=True,
+                )
+            case _:
+                raise NotImplementedError("Language not supported")
+
+    def emit_header_bottom(self, ast):
+        """Emit the bottom header guard"""
+        name = find_xdr_program_name(ast)
+        template = self.environment.get_template("header_bottom.j2")
+        print(template.render(program=name))
+
+    def emit_header_top(self, filename, ast):
+        """Emit the top header guard"""
+        name = find_xdr_program_name(ast)
+        template = self.environment.get_template("header_top.j2")
+        print(
+            template.render(
+                program=name,
+                mtime=time.ctime(os.path.getmtime(filename)),
+            )
+        )
+
+    def emit_source_top(self, filename, ast):
+        """Emit the top source boilerplate"""
+        name = find_xdr_program_name(ast)
+        template = self.environment.get_template("source_top.j2")
+        print(
+            template.render(
+                program=name,
+                mtime=time.ctime(os.path.getmtime(filename)),
+            )
+        )
diff --git a/tools/net/sunrpc/xdrgen/generators/constant.py b/tools/net/sunrpc/xdrgen/generators/constant.py
new file mode 100644
index 000000000000..3d70bd103dc9
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/constant.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR constants"""
+
+from jinja2 import Environment, FileSystemLoader
+
+
+class SourceGenerator:
+    """Generate source code for XDR constants"""
+
+    def __init__(self, language):
+        """Set the output language"""
+        match language:
+            case "C":
+                self.environment = Environment(
+                    loader=FileSystemLoader("templates/C/constants/"),
+                    trim_blocks=True,
+                    lstrip_blocks=True,
+                )
+            case _:
+                raise NotImplementedError("Language not supported")
+
+    def emit_declaration(self, ast_node):
+        """Emit one declaration for a constant"""
+        template = self.environment.get_template("declaration.j2")
+        print(
+            template.render(
+                name=ast_node.name,
+                value=ast_node.value,
+            )
+        )
+
+    def emit_decoder(self, ast_node):
+        """Emit one decoder for a constant"""
+
+    def emit_encoder(self, ast_node):
+        """Emit one encoder for a constant"""
diff --git a/tools/net/sunrpc/xdrgen/generators/enum.py b/tools/net/sunrpc/xdrgen/generators/enum.py
new file mode 100644
index 000000000000..62caa8661f47
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/enum.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR enum types"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from xdr_ast import public_apis
+from common import get_xdr_annotate
+
+
+class SourceGenerator:
+    """Generate source code for XDR enum types"""
+
+    def __init__(self, language):
+        """Set the output language"""
+        match language:
+            case "C":
+                self.environment = Environment(
+                    loader=FileSystemLoader("templates/C/enum/"),
+                    trim_blocks=True,
+                    lstrip_blocks=True,
+                )
+            case _:
+                raise NotImplementedError("Language not supported")
+
+    def emit_declaration(self, ast_node):
+        """Emit one declaration for an enum type"""
+
+        template = self.environment.get_template("declaration/open.j2")
+        print(template.render(name=ast_node.name))
+
+        template = self.environment.get_template("declaration/enumerator.j2")
+        for enumerator in ast_node.enumerators:
+            print(
+                template.render(
+                    name=enumerator.name,
+                    value=enumerator.value,
+                )
+            )
+
+        template = self.environment.get_template("declaration/close.j2")
+        print(template.render(public_apis=public_apis, name=ast_node.name))
+
+    def emit_decoder(self, ast_node):
+        """Emit one decoder function for an enum type"""
+
+        template = self.environment.get_template("decoder/enum.j2")
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+            )
+        )
+
+    def emit_encoder(self, ast_node):
+        """Emit one encoder function for an enum type"""
+
+        template = self.environment.get_template("encoder/enum.j2")
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+            )
+        )
diff --git a/tools/net/sunrpc/xdrgen/generators/program.py b/tools/net/sunrpc/xdrgen/generators/program.py
new file mode 100644
index 000000000000..1825fadd9764
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/program.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code for an RPC program's procedures"""
+
+from jinja2 import Environment, FileSystemLoader
+
+
+def emit_version_declarations(environment, program, version):
+    """Emit C declarations for each RPC version's procedures"""
+    print("")
+    template = environment.get_template("declaration/argument.j2")
+    for procedure in version.procedures:
+        print(
+            template.render(
+                program=program,
+                argument=procedure.argument.type_name,
+            )
+        )
+
+    print("")
+    template = environment.get_template("declaration/result.j2")
+    for procedure in version.procedures:
+        print(template.render(program=program, result=procedure.result.type_name))
+
+
+def emit_version_argument_decoders(environment, program, version):
+    """Emit C decoders for each RPC version's procedures"""
+    arguments = set()
+    for procedure in version.procedures:
+        arguments.add(procedure.argument.type_name)
+
+    template = environment.get_template("decoder/argument.j2")
+    for argument in arguments:
+        print(
+            template.render(
+                program=program,
+                argument=argument,
+            )
+        )
+
+
+def emit_version_result_encoders(environment, program, version):
+    """Emit C encoders for each RPC version's procedures"""
+    results = set()
+    for procedure in version.procedures:
+        results.add(procedure.result.type_name)
+
+    template = environment.get_template("encoder/result.j2")
+    for result in results:
+        print(
+            template.render(
+                program=program,
+                result=result,
+            )
+        )
+
+
+class SourceGenerator:
+    """Generate source code for an RPC program's procedures"""
+
+    def __init__(self, language):
+        """Set the output language"""
+        match language:
+            case "C":
+                self.environment = Environment(
+                    loader=FileSystemLoader("templates/C/program/"),
+                    trim_blocks=True,
+                    lstrip_blocks=True,
+                )
+            case _:
+                raise NotImplementedError("Language not supported")
+
+    def emit_declaration(self, ast_node):
+        """Emit a declarations pair for each of an RPC programs's procedures"""
+        raw_name = ast_node.name
+        program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
+
+        for version in ast_node.versions:
+            emit_version_declarations(self.environment, program, version)
+
+    def emit_decoder(self, ast_node):
+        """Emit all decoder functions for an RPC program's procedures"""
+        raw_name = ast_node.name
+        program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
+
+        for version in ast_node.versions:
+            emit_version_argument_decoders(self.environment, program, version)
+
+    def emit_encoder(self, ast_node):
+        """Emit all encoder functions for an RPC program's procedures"""
+        raw_name = ast_node.name
+        program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
+
+        for version in ast_node.versions:
+            emit_version_result_encoders(self.environment, program, version)
diff --git a/tools/net/sunrpc/xdrgen/generators/struct.py b/tools/net/sunrpc/xdrgen/generators/struct.py
new file mode 100644
index 000000000000..aec908a7952c
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/struct.py
@@ -0,0 +1,435 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR struct types"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from common import get_xdr_annotate
+
+from xdr_ast import _BasicDeclaration, _VariableLengthString
+from xdr_ast import _FixedLengthOpaque, _VariableLengthOpaque
+from xdr_ast import _FixedLengthArray, _VariableLengthArray
+from xdr_ast import _XdrOptionalData, _BuiltInType, public_apis
+
+
+def get_kernel_c_type(type_spec):
+    """Return C type to be used for an XDR built-in type"""
+    xdr_type_to_c_type = {
+        "unsigned_hyper": "u64",
+        "hyper": "s64",
+        "unsigned_long": "u32",
+        "long": "s32",
+        "unsigned_int": "u32",
+        "int": "s32",
+        "bool": "bool",
+    }
+    if isinstance(type_spec, _BuiltInType):
+        return xdr_type_to_c_type[type_spec.type_name]
+    return type_spec.type_name
+
+
+def get_jinja_template(environment, template_type, template_name):
+    """Retrieve a Jinja2 template for emitting source code"""
+    return environment.get_template(template_type + "/" + template_name + ".j2")
+
+
+def emit_struct_member_definition(environment, field):
+    """Emit C definition for one field in one XDR struct"""
+    if isinstance(field, _BasicDeclaration):
+        template = get_jinja_template(environment, "declaration", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=get_kernel_c_type(field.spec),
+                ctype=field.spec.type_decorator,
+            )
+        )
+    elif isinstance(field, _FixedLengthOpaque):
+        template = get_jinja_template(environment, "declaration", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                size=field.size,
+            )
+        )
+    elif isinstance(field, _VariableLengthOpaque):
+        template = get_jinja_template(environment, "declaration", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+            )
+        )
+    elif isinstance(field, _VariableLengthString):
+        template = get_jinja_template(environment, "declaration", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+            )
+        )
+    elif isinstance(field, _FixedLengthArray):
+        template = get_jinja_template(environment, "declaration", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=get_kernel_c_type(field.spec),
+                size=field.size,
+            )
+        )
+    elif isinstance(field, _VariableLengthArray):
+        template = get_jinja_template(environment, "declaration", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=get_kernel_c_type(field.spec),
+                ctype=field.spec.type_decorator,
+            )
+        )
+    elif isinstance(field, _XdrOptionalData):
+        template = get_jinja_template(environment, "declaration", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=get_kernel_c_type(field.spec),
+                ctype=field.spec.type_decorator,
+            )
+        )
+
+
+def emit_struct_definition(environment, ast_node):
+    """Emit one C declaration for a struct type"""
+    template = get_jinja_template(environment, "declaration", "open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.name,
+        )
+    )
+
+    for field in ast_node.fields:
+        emit_struct_member_definition(environment, field)
+
+    template = get_jinja_template(environment, "declaration", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+
+def emit_pointer_definition(environment, ast_node):
+    """Emit one C definition for a pointer type"""
+    template = get_jinja_template(environment, "declaration", "pointer-open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.name,
+        )
+    )
+
+    for field in ast_node.fields[0:-1]:
+        emit_struct_member_definition(environment, field)
+
+    template = get_jinja_template(environment, "declaration", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+
+def emit_struct_member_decoder(environment, field):
+    """Emit C decoder for one field in one XDR struct"""
+    if isinstance(field, _BasicDeclaration):
+        template = get_jinja_template(environment, "decoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                ctype=field.spec.type_decorator,
+            )
+        )
+    elif isinstance(field, _FixedLengthOpaque):
+        template = get_jinja_template(environment, "decoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                size=field.size,
+            )
+        )
+    elif isinstance(field, _VariableLengthOpaque):
+        template = get_jinja_template(environment, "decoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                maxsize=field.maxsize,
+            )
+        )
+    elif isinstance(field, _VariableLengthString):
+        template = get_jinja_template(environment, "decoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                maxsize=field.maxsize,
+            )
+        )
+    elif isinstance(field, _FixedLengthArray):
+        template = get_jinja_template(environment, "decoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                size=field.size,
+                ctype=field.spec.type_decorator,
+            )
+        )
+    elif isinstance(field, _VariableLengthArray):
+        template = get_jinja_template(environment, "decoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                maxsize=field.maxsize,
+                ctype=field.spec.type_decorator,
+            )
+        )
+    elif isinstance(field, _XdrOptionalData):
+        template = get_jinja_template(environment, "decoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                ctype=field.spec.type_decorator,
+            )
+        )
+
+
+def emit_struct_decoder(environment, ast_node):
+    """Emit one decoder function for a struct type"""
+    template = get_jinja_template(environment, "decoder", "open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+    for field in ast_node.fields:
+        emit_struct_member_decoder(environment, field)
+
+    template = get_jinja_template(environment, "decoder", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+        )
+    )
+
+
+# cel: Would be better to just create a new dataclass for pointer types,
+# and copy all of this to generators/pointer.py
+def emit_pointer_decoder(environment, ast_node):
+    """Emit one decoder function for a struct type"""
+    template = get_jinja_template(environment, "decoder", "pointer-open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+    for field in ast_node.fields[0:-1]:
+        emit_struct_member_decoder(environment, field)
+
+    template = get_jinja_template(environment, "decoder", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+        )
+    )
+
+
+def emit_struct_member_encoder(environment, field):
+    """Emit C encoder for one field in one XDR struct"""
+    if isinstance(field, _BasicDeclaration):
+        template = get_jinja_template(environment, "encoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                encode_needs_addressof=field.spec.pass_by_reference,
+            )
+        )
+    elif isinstance(field, _FixedLengthOpaque):
+        template = get_jinja_template(environment, "encoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                size=field.size,
+            )
+        )
+    elif isinstance(field, _VariableLengthOpaque):
+        template = get_jinja_template(environment, "encoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                maxsize=field.maxsize,
+            )
+        )
+    elif isinstance(field, _VariableLengthString):
+        template = get_jinja_template(environment, "encoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                maxsize=field.maxsize,
+            )
+        )
+    elif isinstance(field, _FixedLengthArray):
+        template = get_jinja_template(environment, "encoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                size=field.size,
+                encode_needs_addressof=field.spec.pass_by_reference,
+            )
+        )
+    elif isinstance(field, _VariableLengthArray):
+        template = get_jinja_template(environment, "encoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                maxsize=field.maxsize,
+                encode_needs_addressof=field.spec.pass_by_reference,
+            )
+        )
+    elif isinstance(field, _XdrOptionalData):
+        template = get_jinja_template(environment, "encoder", field.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                name=field.name,
+                type=field.spec.type_name,
+                ctype=field.spec.type_decorator,
+            )
+        )
+
+
+def emit_struct_encoder(environment, ast_node):
+    """Emit one encoder function for a struct type"""
+    template = get_jinja_template(environment, "encoder", "open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+    for field in ast_node.fields:
+        emit_struct_member_encoder(environment, field)
+
+    template = get_jinja_template(environment, "encoder", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+        )
+    )
+
+
+# cel: Would be better to just create a new dataclass for pointer types,
+# and copy all of this to generators/pointer.py
+def emit_pointer_encoder(environment, ast_node):
+    """Emit one encoder function for a struct type"""
+    template = get_jinja_template(environment, "encoder", "pointer-open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+    for field in ast_node.fields[0:-1]:
+        emit_struct_member_encoder(environment, field)
+
+    template = get_jinja_template(environment, "encoder", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+        )
+    )
+
+
+def is_pointer_type(ast_node):
+    """Predicate: returns true if @ast_node should be a pointer type"""
+    last_field = ast_node.fields[-1]
+    if (
+        isinstance(last_field, _XdrOptionalData)
+        and ast_node.name == last_field.spec.type_name
+    ):
+        return True
+    return False
+
+
+class SourceGenerator:
+    """Generate source code for XDR structs"""
+
+    def __init__(self, language):
+        """Set the output language"""
+        match language:
+            case "C":
+                self.environment = Environment(
+                    loader=FileSystemLoader("templates/C/struct/"),
+                    trim_blocks=True,
+                    lstrip_blocks=True,
+                )
+            case _:
+                raise NotImplementedError("Language not supported")
+
+    def emit_declaration(self, ast_node):
+        """Emit one declaration for a struct type"""
+        if is_pointer_type(ast_node):
+            emit_pointer_definition(self.environment, ast_node)
+        else:
+            emit_struct_definition(self.environment, ast_node)
+
+    def emit_decoder(self, ast_node):
+        """Emit one decoder function for a struct type"""
+        if is_pointer_type(ast_node):
+            emit_pointer_decoder(self.environment, ast_node)
+        else:
+            emit_struct_decoder(self.environment, ast_node)
+
+    def emit_encoder(self, ast_node):
+        """Emit one encoder function for a struct type"""
+        if is_pointer_type(ast_node):
+            emit_pointer_encoder(self.environment, ast_node)
+        else:
+            emit_struct_encoder(self.environment, ast_node)
diff --git a/tools/net/sunrpc/xdrgen/generators/typedef.py b/tools/net/sunrpc/xdrgen/generators/typedef.py
new file mode 100644
index 000000000000..615f1e4b0e4d
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/typedef.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR typedefs"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from common import get_xdr_annotate
+
+from xdr_ast import _BasicDeclaration, _VariableLengthString
+from xdr_ast import _FixedLengthOpaque, _VariableLengthOpaque
+from xdr_ast import _FixedLengthArray, _VariableLengthArray
+from xdr_ast import _XdrOptionalData, _XdrVoid, _BuiltInType
+from xdr_ast import public_apis
+
+
+def get_kernel_c_type(type_spec):
+    """Return C type to be used for an XDR built-in type"""
+    xdr_type_to_c_type = {
+        "unsigned_hyper": "u64",
+        "hyper": "s64",
+        "unsigned_long": "u32",
+        "long": "s32",
+        "unsigned_int": "u32",
+        "int": "s32",
+        "bool": "bool",
+    }
+    if isinstance(type_spec, _BuiltInType):
+        return xdr_type_to_c_type[type_spec.type_name]
+    return type_spec.type_name
+
+
+def get_jinja_template(environment, template_type, template_name):
+    """Retrieve a Jinja2 template for emitting source code"""
+    return environment.get_template(template_type + "/" + template_name + ".j2")
+
+
+def emit_type_definition(environment, ast_node):
+    """Emit C declaration for one XDR typedef"""
+    if isinstance(ast_node, _BasicDeclaration):
+        template = get_jinja_template(environment, "declaration", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=get_kernel_c_type(ast_node.spec),
+                ctype=ast_node.spec.type_decorator,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthString):
+        template = get_jinja_template(environment, "declaration", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+            )
+        )
+    elif isinstance(ast_node, _FixedLengthOpaque):
+        template = get_jinja_template(environment, "declaration", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                size=ast_node.size,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthOpaque):
+        template = get_jinja_template(environment, "declaration", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+            )
+        )
+    elif isinstance(ast_node, _FixedLengthArray):
+        template = get_jinja_template(environment, "declaration", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+                size=ast_node.size,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthArray):
+        template = get_jinja_template(environment, "declaration", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+                ctype=ast_node.spec.type_decorator,
+            )
+        )
+    elif isinstance(ast_node, _XdrOptionalData):
+        raise NotImplementedError("<optional_data> typedef not yet implemented")
+    elif isinstance(ast_node, _XdrVoid):
+        raise NotImplementedError("<void> typedef not yet implemented")
+    else:
+        raise NotImplementedError("typedef: type not recognized")
+
+
+def emit_typedef_decoder(environment, ast_node):
+    """Emit C decoder function for one typedef"""
+    if isinstance(ast_node, _BasicDeclaration):
+        template = get_jinja_template(environment, "decoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthString):
+        template = get_jinja_template(environment, "decoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                maxsize=ast_node.maxsize,
+            )
+        )
+    elif isinstance(ast_node, _FixedLengthOpaque):
+        template = get_jinja_template(environment, "decoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                size=ast_node.size,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthOpaque):
+        template = get_jinja_template(environment, "decoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                maxsize=ast_node.maxsize,
+            )
+        )
+    elif isinstance(ast_node, _FixedLengthArray):
+        template = get_jinja_template(environment, "decoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+                size=ast_node.size,
+                ctype=ast_node.spec.type_decorator,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthArray):
+        template = get_jinja_template(environment, "decoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+                maxsize=ast_node.maxsize,
+            )
+        )
+    elif isinstance(ast_node, _XdrOptionalData):
+        raise NotImplementedError("<optional_data> typedef not yet implemented")
+    elif isinstance(ast_node, _XdrVoid):
+        raise NotImplementedError("<void> typedef not yet implemented")
+    else:
+        raise NotImplementedError("typedef: type not recognized")
+
+
+def emit_typedef_encoder(environment, ast_node):
+    """Emit one encoder function for one typedef"""
+    if isinstance(ast_node, _BasicDeclaration):
+        template = get_jinja_template(environment, "encoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+                encode_needs_addressof=ast_node.spec.pass_by_reference,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthString):
+        template = get_jinja_template(environment, "encoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                maxsize=ast_node.maxsize,
+            )
+        )
+    elif isinstance(ast_node, _FixedLengthOpaque):
+        template = get_jinja_template(environment, "encoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                size=ast_node.size,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthOpaque):
+        template = get_jinja_template(environment, "encoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                maxsize=ast_node.maxsize,
+            )
+        )
+    elif isinstance(ast_node, _FixedLengthArray):
+        template = get_jinja_template(environment, "encoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+                size=ast_node.size,
+                encode_needs_addressof=ast_node.spec.pass_by_reference,
+            )
+        )
+    elif isinstance(ast_node, _VariableLengthArray):
+        template = get_jinja_template(environment, "encoder", ast_node.template)
+        print(
+            template.render(
+                annotate=get_xdr_annotate(),
+                public_apis=public_apis,
+                name=ast_node.name,
+                type=ast_node.spec.type_name,
+                maxsize=ast_node.maxsize,
+                encode_needs_addressof=ast_node.spec.pass_by_reference,
+            )
+        )
+    elif isinstance(ast_node, _XdrOptionalData):
+        raise NotImplementedError("<optional_data> typedef not yet implemented")
+    elif isinstance(ast_node, _XdrVoid):
+        raise NotImplementedError("<void> typedef not yet implemented")
+    else:
+        raise NotImplementedError("typedef: type not recognized")
+
+
+class SourceGenerator:
+    """Generate source code for XDR typedefs"""
+
+    def __init__(self, language):
+        """Set the output language"""
+        match language:
+            case "C":
+                self.environment = Environment(
+                    loader=FileSystemLoader("templates/C/typedef/"),
+                    trim_blocks=True,
+                    lstrip_blocks=True,
+                )
+            case _:
+                raise NotImplementedError("Language not supported")
+
+    def emit_declaration(self, ast_node):
+        """Emit one declaration for an XDR typedef"""
+        emit_type_definition(self.environment, ast_node.declaration)
+
+    def emit_decoder(self, ast_node):
+        """Emit one decoder function for an XDR typedef"""
+        emit_typedef_decoder(self.environment, ast_node.declaration)
+
+    def emit_encoder(self, ast_node):
+        """Emit one encoder function for an XDR typedef"""
+        emit_typedef_encoder(self.environment, ast_node.declaration)
diff --git a/tools/net/sunrpc/xdrgen/generators/union.py b/tools/net/sunrpc/xdrgen/generators/union.py
new file mode 100644
index 000000000000..ea638c1b0d4a
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/union.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR unions"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from common import get_xdr_annotate
+
+from xdr_ast import _BasicDeclaration, _XdrVoid, public_apis
+
+
+def get_jinja_template(environment, template_type, template_name):
+    """Retrieve a Jinja2 template for emitting source code"""
+    return environment.get_template(template_type + "/" + template_name + ".j2")
+
+
+def emit_union_switch_spec_definition(environment, ast_node):
+    """Emit C declaration for an XDR union's discriminant"""
+    assert isinstance(ast_node, _BasicDeclaration)
+    template = get_jinja_template(environment, "declaration", "switch_spec")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.name,
+            type=ast_node.spec.type_name,
+            ctype=ast_node.spec.type_decorator,
+        )
+    )
+
+
+def emit_union_case_spec_definition(environment, ast_node):
+    """Emit C declaration for an XDR union's case arm"""
+    if isinstance(ast_node.arm, _XdrVoid):
+        return
+    assert isinstance(ast_node.arm, _BasicDeclaration)
+    template = get_jinja_template(environment, "declaration", "case_spec")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.arm.name,
+            type=ast_node.arm.spec.type_name,
+            ctype=ast_node.arm.spec.type_decorator,
+        )
+    )
+
+
+def emit_union_declaration(environment, ast_node):
+    """Emit one C union definition"""
+    template = get_jinja_template(environment, "declaration", "open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.name,
+        )
+    )
+
+    emit_union_switch_spec_definition(environment, ast_node.discriminant)
+
+    for case in ast_node.cases:
+        emit_union_case_spec_definition(environment, case)
+
+    if ast_node.default is not None:
+        emit_union_case_spec_definition(environment, ast_node.default)
+
+    template = get_jinja_template(environment, "declaration", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+
+def emit_union_switch_spec_decoder(environment, ast_node):
+    """Emit C decoder for an XDR union's discriminant"""
+    assert isinstance(ast_node, _BasicDeclaration)
+    template = get_jinja_template(environment, "decoder", "switch_spec")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.name,
+            type=ast_node.spec.type_name,
+        )
+    )
+
+
+def emit_union_case_spec_decoder(environment, ast_node):
+    """Emit C decoder functions for an XDR union's case arm"""
+
+    if isinstance(ast_node.arm, _XdrVoid):
+        return
+
+    template = get_jinja_template(environment, "decoder", "case_spec")
+    for case in ast_node.values:
+        match case:
+            case "TRUE":
+                print(template.render(case="true"))
+            case "FALSE":
+                print(template.render(case="false"))
+            case _:
+                print(template.render(case=case))
+
+    assert isinstance(ast_node.arm, _BasicDeclaration)
+    template = get_jinja_template(environment, "decoder", ast_node.arm.template)
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.arm.name,
+            type=ast_node.arm.spec.type_name,
+            ctype=ast_node.arm.spec.type_decorator,
+        )
+    )
+
+    template = get_jinja_template(environment, "decoder", "break")
+    print(template.render())
+
+
+def emit_union_default_spec_decoder(environment, ast_node):
+    """Emit C decoder function for an XDR union's default arm"""
+    default_case = ast_node.default
+
+    # Avoid a gcc warning about a default case with boolean discriminant
+    if default_case is None and ast_node.discriminant.spec.type_name == "bool":
+        return
+
+    template = get_jinja_template(environment, "decoder", "default_spec")
+    print(template.render())
+
+    if default_case is None or isinstance(default_case.arm, _XdrVoid):
+        template = get_jinja_template(environment, "decoder", "break")
+        print(template.render())
+        return
+
+    assert isinstance(default_case.arm, _BasicDeclaration)
+    template = get_jinja_template(environment, "decoder", default_case.arm.template)
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=default_case.arm.name,
+            type=default_case.arm.spec.type_name,
+            ctype=default_case.arm.spec.type_decorator,
+        )
+    )
+
+
+def emit_union_decoder(environment, ast_node):
+    """Emit one C union decoder"""
+    template = get_jinja_template(environment, "decoder", "open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+    emit_union_switch_spec_decoder(environment, ast_node.discriminant)
+
+    for case in ast_node.cases:
+        emit_union_case_spec_decoder(environment, case)
+
+    emit_union_default_spec_decoder(environment, ast_node)
+
+    template = get_jinja_template(environment, "decoder", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+        )
+    )
+
+
+def emit_union_switch_spec_encoder(environment, ast_node):
+    """Emit C encoder for an XDR union's discriminant"""
+    assert isinstance(ast_node, _BasicDeclaration)
+    template = get_jinja_template(environment, "encoder", "switch_spec")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.name,
+            type=ast_node.spec.type_name,
+        )
+    )
+
+
+def emit_union_case_spec_encoder(environment, ast_node):
+    """Emit C encoder functions for an XDR union's case arm"""
+
+    if isinstance(ast_node.arm, _XdrVoid):
+        return
+
+    template = get_jinja_template(environment, "encoder", "case_spec")
+    for case in ast_node.values:
+        match case:
+            case "TRUE":
+                print(template.render(case="true"))
+            case "FALSE":
+                print(template.render(case="false"))
+            case _:
+                print(template.render(case=case))
+
+    assert isinstance(ast_node.arm, _BasicDeclaration)
+    template = get_jinja_template(environment, "encoder", ast_node.arm.template)
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=ast_node.arm.name,
+            type=ast_node.arm.spec.type_name,
+            encode_needs_addressof=ast_node.arm.spec.pass_by_reference,
+        )
+    )
+
+    template = get_jinja_template(environment, "encoder", "break")
+    print(template.render())
+
+
+def emit_union_default_spec_encoder(environment, ast_node):
+    """Emit C encoder function for an XDR union's default arm"""
+    default_case = ast_node.default
+
+    # Avoid a gcc warning about a default case with boolean discriminant
+    if default_case is None and ast_node.discriminant.spec.type_name == "bool":
+        return
+
+    template = get_jinja_template(environment, "encoder", "default_spec")
+    print(template.render())
+
+    if default_case is None or isinstance(default_case.arm, _XdrVoid):
+        template = get_jinja_template(environment, "encoder", "break")
+        print(template.render())
+        return
+
+    assert isinstance(default_case.arm, _BasicDeclaration)
+    template = get_jinja_template(environment, "encoder", default_case.arm.template)
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            name=default_case.arm.name,
+            type=default_case.arm.spec.type_name,
+            encode_needs_addressof=default_case.arm.spec.pass_by_reference,
+        )
+    )
+
+
+def emit_union_encoder(environment, ast_node):
+    """Emit one C union encoder"""
+    template = get_jinja_template(environment, "encoder", "open")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+            public_apis=public_apis,
+            name=ast_node.name,
+        )
+    )
+
+    emit_union_switch_spec_encoder(environment, ast_node.discriminant)
+
+    for case in ast_node.cases:
+        emit_union_case_spec_encoder(environment, case)
+
+    emit_union_default_spec_encoder(environment, ast_node)
+
+    template = get_jinja_template(environment, "encoder", "close")
+    print(
+        template.render(
+            annotate=get_xdr_annotate(),
+        )
+    )
+
+
+class SourceGenerator:
+    """Generate source code for XDR unions"""
+
+    def __init__(self, language):
+        """Set the output language"""
+        match language:
+            case "C":
+                self.environment = Environment(
+                    loader=FileSystemLoader("templates/C/union/"),
+                    trim_blocks=True,
+                    lstrip_blocks=True,
+                )
+            case _:
+                raise NotImplementedError("Language not supported")
+
+    def emit_declaration(self, ast_node):
+        """Emit one declaration for an XDR union"""
+        emit_union_declaration(self.environment, ast_node)
+
+    def emit_decoder(self, ast_node):
+        """Emit one decoder function for an XDR union"""
+        emit_union_decoder(self.environment, ast_node)
+
+    def emit_encoder(self, ast_node):
+        """Emit one encoder function for an XDR union"""
+        emit_union_encoder(self.environment, ast_node)
diff --git a/tools/net/sunrpc/xdrgen/subcmds/__init__.py b/tools/net/sunrpc/xdrgen/subcmds/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/tools/net/sunrpc/xdrgen/subcmds/header.py b/tools/net/sunrpc/xdrgen/subcmds/header.py
new file mode 100755
index 000000000000..a3f2bc475715
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/subcmds/header.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+import logging
+
+from lark import logger
+
+from generators import boilerplate, constant, enum
+from generators import program, typedef, struct, union
+from xdr_ast import transform_parse_tree, _RpcProgram, _XdrEnum
+from xdr_ast import _XdrConstant, _XdrTypedef, _XdrStruct, _XdrUnion
+from xdr_parse import xdr_parser
+
+logger.setLevel(logging.INFO)
+
+
+def generate_header(filename, ast, language):
+    """Generate and emit a header file"""
+
+    sg = boilerplate.SourceGenerator(language)
+    sg.emit_header_top(filename, ast)
+
+    for definition in ast.definitions:
+        if isinstance(definition.value, _XdrConstant):
+            sg = constant.SourceGenerator(language)
+        elif isinstance(definition.value, _XdrEnum):
+            sg = enum.SourceGenerator(language)
+        elif isinstance(definition.value, _XdrTypedef):
+            sg = typedef.SourceGenerator(language)
+        elif isinstance(definition.value, _XdrStruct):
+            sg = struct.SourceGenerator(language)
+        elif isinstance(definition.value, _XdrUnion):
+            sg = union.SourceGenerator(language)
+        elif isinstance(definition.value, _RpcProgram):
+            sg = program.SourceGenerator(language)
+        else:
+            continue
+        sg.emit_declaration(definition.value)
+
+    generator = boilerplate.SourceGenerator(language)
+    generator.emit_header_bottom(ast)
+
+
+def handle_parse_error(e):
+    """Simple parse error reporting, no recovery attempted"""
+    print(e)
+    return True
+
+
+def subcmd(args):
+    """Generate definitions and declarations"""
+
+    parser = xdr_parser()
+    with open(args.filename, encoding="utf-8") as f:
+        parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+        ast = transform_parse_tree(parse_tree)
+        generate_header(args.filename, ast, args.language)
+
+    return 0
diff --git a/tools/net/sunrpc/xdrgen/subcmds/lint.py b/tools/net/sunrpc/xdrgen/subcmds/lint.py
new file mode 100755
index 000000000000..d0ace248adb7
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/subcmds/lint.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+import logging
+
+from lark import logger
+from xdr_parse import xdr_parser
+
+logger.setLevel(logging.DEBUG)
+
+
+def handle_parse_error(e):
+    """Simple parse error reporting, no recovery attempted"""
+    print(e)
+    return True
+
+
+def subcmd(args):
+    """Lexical and syntax check of an XDR specification"""
+
+    parser = xdr_parser()
+    with open(args.filename, encoding="utf-8") as f:
+        parser.parse(f.read(), on_error=handle_parse_error)
+
+    return 0
diff --git a/tools/net/sunrpc/xdrgen/subcmds/source.py b/tools/net/sunrpc/xdrgen/subcmds/source.py
new file mode 100755
index 000000000000..b7d0bb4a692d
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/subcmds/source.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+import logging
+
+from lark import logger
+
+from generators import boilerplate, enum, program
+from generators import typedef, struct, union
+from xdr_ast import transform_parse_tree, _XdrEnum, _XdrTypedef
+from xdr_ast import _XdrStruct, _XdrUnion, _RpcProgram
+from xdr_parse import xdr_parser
+
+logger.setLevel(logging.INFO)
+
+
+def emit_source_decoder(ast_node, language):
+    """Emit one XDR decoder function for a source file"""
+    if isinstance(ast_node, _XdrEnum):
+        sg = enum.SourceGenerator(language)
+    elif isinstance(ast_node, _XdrTypedef):
+        sg = typedef.SourceGenerator(language)
+    elif isinstance(ast_node, _XdrStruct):
+        sg = struct.SourceGenerator(language)
+    elif isinstance(ast_node, _XdrUnion):
+        sg = union.SourceGenerator(language)
+    elif isinstance(ast_node, _RpcProgram):
+        sg = program.SourceGenerator(language)
+    else:
+        return
+    sg.emit_decoder(ast_node)
+
+
+def emit_source_encoder(ast_node, language):
+    """Emit one XDR encoder function for a source file"""
+    if isinstance(ast_node, _XdrEnum):
+        sg = enum.SourceGenerator(language)
+    elif isinstance(ast_node, _XdrTypedef):
+        sg = typedef.SourceGenerator(language)
+    elif isinstance(ast_node, _XdrStruct):
+        sg = struct.SourceGenerator(language)
+    elif isinstance(ast_node, _XdrUnion):
+        sg = union.SourceGenerator(language)
+    elif isinstance(ast_node, _RpcProgram):
+        sg = program.SourceGenerator(language)
+    else:
+        return
+    sg.emit_encoder(ast_node)
+
+
+def generate_source(filename, ast, language):
+    """Generate and emit a C source file"""
+
+    sg = boilerplate.SourceGenerator(language)
+    sg.emit_source_top(filename, ast)
+
+    for definition in ast.definitions:
+        emit_source_decoder(definition.value, language)
+
+    for definition in ast.definitions:
+        emit_source_encoder(definition.value, language)
+
+
+def handle_parse_error(e):
+    """Simple parse error reporting, no recovery attempted"""
+    print(e)
+    return True
+
+
+def subcmd(args):
+    """Generate encoder and decoder functions"""
+
+    parser = xdr_parser()
+    with open(args.filename, encoding="utf-8") as f:
+        parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+        ast = transform_parse_tree(parse_tree)
+        generate_source(args.filename, ast, args.language)
+
+    return 0
diff --git a/tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_bottom.j2 b/tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_bottom.j2
new file mode 100644
index 000000000000..5391a867b16f
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_bottom.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+#endif /* _LINUX_{{ program.upper() }}_XDRGEN_H */
diff --git a/tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_top.j2 b/tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_top.j2
new file mode 100644
index 000000000000..216f10fc7250
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_top.j2
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Generated by xdrgen. Manual edits will be lost. */
+/* XDR specification modification time: {{ mtime }} */
+
+#ifndef _LINUX_{{ program.upper() }}_XDRGEN_H
+#define _LINUX_{{ program.upper() }}_XDRGEN_H
+
+#include <linux/types.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/sunrpc/xdrgen-builtins.h>
diff --git a/tools/net/sunrpc/xdrgen/templates/C/boilerplate/source_top.j2 b/tools/net/sunrpc/xdrgen/templates/C/boilerplate/source_top.j2
new file mode 100644
index 000000000000..db46f9bd8354
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/boilerplate/source_top.j2
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+// Generated by xdrgen. Manual edits will be lost.
+// XDR specification modification time: {{ mtime }}
+
+#include "{{ program }}xdr_gen.h"
diff --git a/tools/net/sunrpc/xdrgen/templates/C/constants/declaration.j2 b/tools/net/sunrpc/xdrgen/templates/C/constants/declaration.j2
new file mode 100644
index 000000000000..40b7db7944d4
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/constants/declaration.j2
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+enum {
+	{{ name }} = {{ value }}
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2
new file mode 100644
index 000000000000..dd3e880d0fa2
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enumerator.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enumerator.j2
new file mode 100644
index 000000000000..ff0b893b8b14
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enumerator.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	{{ name }} = {{ value }},
diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/open.j2
new file mode 100644
index 000000000000..b25335221d48
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/open.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+enum {{ name }} {
diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
new file mode 100644
index 000000000000..341d829afeda
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
@@ -0,0 +1,19 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* enum {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr)
+{
+	u32 val;
+
+	if (xdr_stream_decode_u32(xdr, &val) < 0)
+		return false;
+	*ptr = val;
+	return true;
+}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
new file mode 100644
index 000000000000..bd0a770e50f2
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
@@ -0,0 +1,14 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* enum {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value)
+{
+	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2
new file mode 100644
index 000000000000..4364fed19162
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2
new file mode 100644
index 000000000000..e0ea1e849910
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2
new file mode 100644
index 000000000000..0b1709cca0d4
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2
@@ -0,0 +1,21 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+/**
+ * {{ program }}_svc_decode_{{ argument }} - Decode a {{ argument }} argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ *   %true: procedure arguments decoded successfully
+ *   %false: decode failed
+ */
+bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+{% if argument == 'void' %}
+	return xdrgen_decode_void(xdr);
+{% else %}
+	struct {{ argument }} *argp = rqstp->rq_argp;
+
+	return xdrgen_decode_{{ argument }}(xdr, argp);
+{% endif %}
+}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2
new file mode 100644
index 000000000000..6fc61a5d47b7
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2
@@ -0,0 +1,21 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+/**
+ * {{ program }}_svc_encode_{{ result }} - Encode a {{ result }} result
+ * @rqstp: RPC transaction context
+ * @xdr: target XDR data stream
+ *
+ * Return values:
+ *   %true: procedure results encoded successfully
+ *   %false: encode failed
+ */
+bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+{% if result == 'void' %}
+	return xdrgen_encode_void(xdr);
+{% else %}
+	struct {{ result }} *resp = rqstp->rq_resp;
+
+	return xdrgen_encode_{{ result }}(xdr, resp);
+{% endif %}
+}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/basic.j2
new file mode 100644
index 000000000000..d3a5607d30da
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/basic.j2
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* (basic) */
+{% endif %}
+	{{ ctype }}{{ type }} {{ name }};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2
new file mode 100644
index 000000000000..f40098266f7f
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_array.j2
new file mode 100644
index 000000000000..66be836826a0
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_array.j2
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* (fixed-length array) */
+{% endif %}
+	{{ type }} {{ name }}[{{ size }}];
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_opaque.j2
new file mode 100644
index 000000000000..0daba19aa0f0
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_opaque.j2
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* (fixed-length opaque) */
+{% endif %}
+	u8 {{ name }}[{{ size }}];
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/open.j2
new file mode 100644
index 000000000000..07cbf5424546
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/open.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* struct {{ name }} */
+{% endif %}
+struct {{ name }} {
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/optional_data.j2
new file mode 100644
index 000000000000..7e52afbb1f3e
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/optional_data.j2
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* (optional data) */
+{% endif %}
+	{{ ctype }}{{ type }} *{{ name }};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/pointer-open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/pointer-open.j2
new file mode 100644
index 000000000000..bc886b818d85
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/pointer-open.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* pointer {{ name }} */
+{% endif %}
+struct {{ name }} {
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_array.j2
new file mode 100644
index 000000000000..aa3e62c2cd3c
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_array.j2
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* (variable-length array) */
+{% endif %}
+	struct {
+		u32 count;
+		{{ ctype }}{{ type }} *element;
+	} {{ name }};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_opaque.j2
new file mode 100644
index 000000000000..4d0cd84be3db
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_opaque.j2
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* (variable-length opaque) */
+{% endif %}
+	opaque {{ name }};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_string.j2
new file mode 100644
index 000000000000..2de2feec77db
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_string.j2
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* (variable-length string) */
+{% endif %}
+	string {{ name }};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2
new file mode 100644
index 000000000000..cde4ab53f4be
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (basic) */
+{% endif %}
+	if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}))
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2
new file mode 100644
index 000000000000..5bf010665f84
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2
new file mode 100644
index 000000000000..cfd64217ad82
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (fixed-length array) */
+{% endif %}
+	for (u32 i = 0; i < {{ size }}; i++) {
+		if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0)
+			return false;
+	}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2
new file mode 100644
index 000000000000..b4695ece1884
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (fixed-length opaque) */
+{% endif %}
+	if (xdr_stream_decode_opaque_fixed(xdr, ptr->{{ name }}, {{ size }}) < 0)
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2
new file mode 100644
index 000000000000..289e67259f55
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* struct {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
+{
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2
new file mode 100644
index 000000000000..b6834299a04b
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (optional data) */
+{% endif %}
+	if (!xdrgen_decode_{{ type }}(xdr, ptr->{{ name }}))
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/pointer-open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/pointer-open.j2
new file mode 100644
index 000000000000..c093d9e3c9ad
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/pointer-open.j2
@@ -0,0 +1,22 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* pointer {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
+{
+	bool opted;
+
+{% if annotate %}
+	/* opted */
+{% endif %}
+	if (!xdrgen_decode_bool(xdr, &opted))
+		return false;
+	if (!opted)
+		return true;
+
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2
new file mode 100644
index 000000000000..d318a09cad12
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2
@@ -0,0 +1,13 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (variable-length array) */
+{% endif %}
+	if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT)
+		return false;
+{% if maxsize != 0 %}
+	if (ptr->{{ name }}.count > {{ maxsize }})
+		return false;
+{% endif %}
+	for (u32 i = 0; i < ptr->{{ name }}.count; i++)
+		if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.element[i]))
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2
new file mode 100644
index 000000000000..9a814de54ae8
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (variable-length opaque) */
+{% endif %}
+	if (!xdrgen_decode_opaque(xdr, (opaque *)ptr, {{ maxsize }}))
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2
new file mode 100644
index 000000000000..12d20b143b43
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (variable-length string) */
+{% endif %}
+	if (!xdrgen_decode_string(xdr, (string *)ptr, {{ maxsize }}))
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2
new file mode 100644
index 000000000000..e67cdc36809a
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2
@@ -0,0 +1,10 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (basic) */
+{% endif %}
+{% if encode_needs_addressof %}
+	if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}))
+{% else %}
+	if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
+{% endif %}
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2
new file mode 100644
index 000000000000..5bf010665f84
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2
new file mode 100644
index 000000000000..f9d40953e5b5
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (fixed-length array) */
+{% endif %}
+	for (u32 i = 0; i < {{ size }}; i++) {
+{% if encode_needs_addressof %}
+		if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0)
+{% else %}
+		if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0)
+{% endif %}
+			return false;
+	}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2
new file mode 100644
index 000000000000..07bc91919898
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (fixed-length opaque) */
+{% endif %}
+	if (xdr_stream_encode_opaque_fixed(xdr, value->{{ name }}, {{ size }}) < 0)
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2
new file mode 100644
index 000000000000..2286a3adf82a
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* struct {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value)
+{
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2
new file mode 100644
index 000000000000..16fb3e09bba1
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (optional data) */
+{% endif %}
+	if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/pointer-open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/pointer-open.j2
new file mode 100644
index 000000000000..d67fae200261
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/pointer-open.j2
@@ -0,0 +1,20 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* pointer {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value)
+{
+{% if annotate %}
+	/* opted */
+{% endif %}
+	if (!xdrgen_encode_bool(xdr, value != NULL))
+		return false;
+	if (!value)
+		return true;
+
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2
new file mode 100644
index 000000000000..880097d8ee5e
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2
@@ -0,0 +1,15 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (variable-length array) */
+{% endif %}
+	if (value->{{ name }}.count > {{ maxsize }})
+		return false;
+	if (xdr_stream_encode_u32(xdr, value->{{ name }}.count) != XDR_UNIT)
+		return false;
+	for (u32 i = 0; i < value->{{ name }}.count; i++)
+{% if encode_needs_addressof %}
+		if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}.element[i]))
+{% else %}
+		if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}.element[i]))
+{% endif %}
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2
new file mode 100644
index 000000000000..1d477c2d197a
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (variable-length opaque) */
+{% endif %}
+	if (value->{{ name }}.len > {{ maxsize }})
+		return false;
+	if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2
new file mode 100644
index 000000000000..cf65b71eaef3
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* member {{ name }} (variable-length string) */
+{% endif %}
+	if (value->{{ name }}.len > {{ maxsize }})
+		return false;
+	if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
+		return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2
new file mode 100644
index 000000000000..a74e3b2a6102
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (basic) */
+{% endif %}
+typedef {{ ctype }}{{ type }} {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ name }} value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2
new file mode 100644
index 000000000000..2ab394f3b1bc
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (fixed-length array) */
+{% endif %}
+typedef {{ type }}{{ name }}[{{ size }}];
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2
new file mode 100644
index 000000000000..b81773efdb54
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (fixed-length opaque) */
+{% endif %}
+typedef u8 {{ name }}[{{ size }}];
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2
new file mode 100644
index 000000000000..b6f67a4007ab
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2
@@ -0,0 +1,14 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (variable-length array) */
+{% endif %}
+typedef struct {
+	u32 count;
+	{{ ctype }}{{ type }} *element;
+} {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2
new file mode 100644
index 000000000000..badf2fec1e5d
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (variable-length opaque) */
+{% endif %}
+typedef opaque {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2
new file mode 100644
index 000000000000..1f8b24c4137a
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (variable-length string) */
+{% endif %}
+typedef string {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2
new file mode 100644
index 000000000000..da4709403dc9
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr)
+{
+{% if annotate %}
+	/* (basic) */
+{% endif %}
+	return xdrgen_decode_{{ type }}(xdr, ptr);
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2
new file mode 100644
index 000000000000..e4791eb55417
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2
@@ -0,0 +1,25 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+	/* (fixed-length array) */
+{% endif %}
+	for (u32 i = 0; i < {{ size }}; i++) {
+{%- if ctype == '' %}
+		if (xdrgen_decode_{{ type }}(xdr, ptr->items[i]) < 0)
+{% else %}
+		if (xdrgen_decode_{{ type }}(xdr, &ptr->items[i]) < 0)
+{% endif %}
+			return false;
+	}
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2
new file mode 100644
index 000000000000..7cc2a474f583
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+	/* (fixed-length opaque) */
+{% endif %}
+	return xdr_stream_decode_opaque_fixed(xdr, ptr, {{ size }}) >= 0;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2
new file mode 100644
index 000000000000..54bc0a0fd9f9
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2
@@ -0,0 +1,26 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+	/* (variable-length array) */
+{% endif %}
+	if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
+		return false;
+{% if maxsize != 0 %}
+	if (ptr->count > {{ maxsize }})
+		return false;
+{% endif %}
+	for (u32 i = 0; i < ptr->count; i++)
+		if (!xdrgen_decode_{{ type }}(xdr, &ptr->element[i]))
+			return false;
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2
new file mode 100644
index 000000000000..0cabb94c0f8a
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+	/* (variable-length opaque) */
+{% endif %}
+	return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2
new file mode 100644
index 000000000000..4f12ac002aa6
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+	/* (variable-length string) */
+{% endif %}
+	return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2
new file mode 100644
index 000000000000..411644c7a289
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2
@@ -0,0 +1,21 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+	/* (basic) */
+{% endif %}
+{% if encode_needs_addressof %}
+	return xdrgen_encode_{{ type }}(xdr, &value);
+{% else %}
+	return xdrgen_encode_{{ type }}(xdr, value);
+{% endif %}
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2
new file mode 100644
index 000000000000..ec6823b0c2b3
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2
@@ -0,0 +1,25 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+	/* (fixed-length array) */
+{% endif %}
+	for (u32 i = 0; i < {{ size }}; i++) {
+{% elif encode_needs_deref %}
+		if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0)
+{% else %}
+		if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0)
+{% endif %}
+			return false;
+	}
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2
new file mode 100644
index 000000000000..2b51b61f3c85
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+	/* (fixed-length opaque) */
+{% endif %}
+	return xdr_stream_encode_opaque_fixed(xdr, value, {{ size }}) >= 0;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2
new file mode 100644
index 000000000000..f9230a32fd96
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2
@@ -0,0 +1,30 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+	/* (variable-length array) */
+{% endif %}
+{% if maxsize != 0 %}
+	if (unlikely(value.count > {{ maxsize }}))
+		return false;
+{% endif %}
+	if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
+		return false;
+	for (u32 i = 0; i < value.count; i++)
+{% if encode_needs_addressof %}
+		if (!xdrgen_encode_{{ type }}(xdr, &value.element[i]))
+{% else %}
+		if (!xdrgen_encode_{{ type }}(xdr, value.element[i]))
+{% endif %}
+			return false;
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2
new file mode 100644
index 000000000000..17e97fe1f43b
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+	/* (variable-length opaque) */
+{% endif %}
+	return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2
new file mode 100644
index 000000000000..8747925292fe
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+	/* (variable-length string) */
+{% endif %}
+	return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/declaration/case_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/case_spec.j2
new file mode 100644
index 000000000000..6bfba6c53996
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/case_spec.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+		{{ ctype }}{{ type }} {{ name }};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2
new file mode 100644
index 000000000000..01d716d0099e
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	} u;
+};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *ptr);
+{%- endif -%}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/declaration/default_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/default_spec.j2
new file mode 100644
index 000000000000..6bfba6c53996
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/default_spec.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+		{{ ctype }}{{ type }} {{ name }};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/declaration/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/open.j2
new file mode 100644
index 000000000000..20fcfd1fc4e5
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/open.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* union {{ name }} */
+{% endif %}
+struct {{ name }} {
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/declaration/switch_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/switch_spec.j2
new file mode 100644
index 000000000000..53639b006b0c
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/declaration/switch_spec.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	{{ ctype }}{{ type }} {{ name }};
+	union {
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2
new file mode 100644
index 000000000000..4d97cc5395eb
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+		/* member {{ name }} (basic) */
+{% endif %}
+		if (!xdrgen_decode_{{ type }}(xdr, &ptr->u.{{ name }}))
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2
new file mode 100644
index 000000000000..b286d1407029
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+		break;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2
new file mode 100644
index 000000000000..5fa2163f0a74
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	case {{ case }}:
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2
new file mode 100644
index 000000000000..fdc2dfd1843b
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2
@@ -0,0 +1,4 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	}
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2
new file mode 100644
index 000000000000..044a002d0589
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	default:
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2
new file mode 100644
index 000000000000..eb9941376e49
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* union {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
+{
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2
new file mode 100644
index 000000000000..e4476f5fd8d3
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+		/* member {{ name }} (optional data) */
+{% endif %}
+		if (!xdrgen_decode_{{ type }}(xdr, &ptr->u.{{ name }}))
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2
new file mode 100644
index 000000000000..99b3067ef617
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* discriminant {{ name }} */
+{% endif %}
+	if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}))
+		return false;
+	switch (ptr->{{ name }}) {
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2
new file mode 100644
index 000000000000..eee2b9a68e27
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2
@@ -0,0 +1,13 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+		/* member {{ name }} (variable-length array) */
+{% endif %}
+		if (xdr_stream_decode_u32(xdr, &count) != XDR_UNIT)
+			return false;
+		if (count > {{ maxsize }})
+			return false;
+		for (u32 i = 0; i < count; i++) {
+			if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0)
+				return false;
+		}
+		ptr->{{ name }}.len = count;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2
new file mode 100644
index 000000000000..c9d88ed29c78
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+		/* member {{ name }} (variable-length opaque) */
+{% endif %}
+		if (!xdrgen_decode_opaque(xdr, (struct opaque *)ptr->u.{{ name }}, {{ maxsize }}))
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2
new file mode 100644
index 000000000000..83b6e5a14e7f
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+		/* member {{ name }} (variable-length string) */
+{% endif %}
+		if (!xdrgen_decode_string(xdr, (struct string *)ptr->u.{{ name }}, {{ maxsize }}))
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2
new file mode 100644
index 000000000000..65205ce37b36
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+		if (!xdrgen_decode_void(xdr))
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2
new file mode 100644
index 000000000000..29a440265824
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2
@@ -0,0 +1,10 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+		/* member {{ name }} (basic) */
+{% endif %}
+{% if encode_needs_addressof %}
+		if (!xdrgen_encode_{{ type }}(xdr, &ptr->u.{{ name }}))
+{% else %}
+		if (!xdrgen_encode_{{ type }}(xdr, ptr->u.{{ name }}))
+{% endif %}
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2
new file mode 100644
index 000000000000..b286d1407029
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+		break;
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2
new file mode 100644
index 000000000000..5fa2163f0a74
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	case {{ case }}:
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2
new file mode 100644
index 000000000000..fdc2dfd1843b
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2
@@ -0,0 +1,4 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	}
+	return true;
+};
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2
new file mode 100644
index 000000000000..044a002d0589
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+	default:
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2
new file mode 100644
index 000000000000..e5a206df10c6
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* union {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *ptr)
+{
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2
new file mode 100644
index 000000000000..c8c3ecbe038b
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+	/* discriminant {{ name }} */
+{% endif %}
+	if (!xdrgen_encode_{{ type }}(xdr, ptr->{{ name }}))
+		return false;
+	switch (ptr->{{ name }}) {
diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2
new file mode 100644
index 000000000000..84e7c2127d75
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+		if (!xdrgen_encode_void(xdr))
+			return false;
diff --git a/tools/net/sunrpc/xdrgen/tests/test.x b/tools/net/sunrpc/xdrgen/tests/test.x
new file mode 100644
index 000000000000..90c8587f6fe5
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/tests/test.x
@@ -0,0 +1,36 @@
+/* Sample XDR specification from RFC 1832 Section 5.5 */
+
+const MAXUSERNAME = 32;     /* max length of a user name */
+const MAXFILELEN = 65535;   /* max length of a file      */
+const MAXNAMELEN = 255;     /* max length of a file name */
+
+/*
+ * Types of files:
+ */
+enum filekind {
+   TEXT = 0,       /* ascii data */
+   DATA = 1,       /* raw data   */
+   EXEC = 2        /* executable */
+};
+
+/*
+ * File information, per kind of file:
+ */
+union filetype switch (filekind kind) {
+case TEXT:
+   void;                           /* no extra information */
+case DATA:
+   string creator<MAXNAMELEN>;     /* data creator         */
+case EXEC:
+   string interpretor<MAXNAMELEN>; /* program interpretor  */
+};
+
+/*
+ * A complete file:
+ */
+struct file {
+   string filename<MAXNAMELEN>; /* name of file    */
+   filetype type;               /* info about file */
+   string owner<MAXUSERNAME>;   /* owner of file   */
+   opaque data<MAXFILELEN>;     /* file data       */
+};
diff --git a/tools/net/sunrpc/xdrgen/xdr b/tools/net/sunrpc/xdrgen/xdr
new file mode 120000
index 000000000000..95c127ec85a6
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/xdr
@@ -0,0 +1 @@
+../../../../Documentation/sunrpc/xdr
\ No newline at end of file
diff --git a/tools/net/sunrpc/xdrgen/xdr.ebnf b/tools/net/sunrpc/xdrgen/xdr.ebnf
new file mode 100644
index 000000000000..eedcca7e98b9
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/xdr.ebnf
@@ -0,0 +1,117 @@
+// A Lark grammar for the XDR specification language based on
+// https://tools.ietf.org/html/rfc4506 Section 6.3
+
+declaration             : "opaque" identifier "[" value "]"            -> fixed_length_opaque
+                        | "opaque" identifier "<" [ value ] ">"        -> variable_length_opaque
+                        | "string" identifier "<" [ value ] ">"        -> variable_length_string
+                        | type_specifier identifier "[" value "]"      -> fixed_length_array
+                        | type_specifier identifier "<" [ value ] ">"  -> variable_length_array
+                        | type_specifier "*" identifier                -> optional_data
+                        | type_specifier identifier                    -> basic
+                        | "void"                                       -> void
+
+value                   : decimal_constant
+                        | hexadecimal_constant
+                        | octal_constant
+                        | identifier
+
+constant                : decimal_constant | hexadecimal_constant | octal_constant
+
+type_specifier          : unsigned_hyper
+                        | unsigned_long
+                        | unsigned_int
+                        | hyper
+                        | long
+                        | int
+                        | float
+                        | double
+                        | quadruple
+                        | bool
+                        | enum_type_spec
+                        | struct_type_spec
+                        | union_type_spec
+                        | identifier
+
+unsigned_hyper          : "unsigned" "hyper"
+unsigned_long           : "unsigned" "long"
+unsigned_int            : "unsigned" "int"
+hyper                   : "hyper"
+long                    : "long"
+int                     : "int"
+float                   : "float"
+double                  : "double"
+quadruple               : "quadruple"
+bool                    : "bool"
+
+enum_type_spec          : "enum" enum_body
+
+enum_body               : "{" ( identifier "=" value ) ( "," identifier "=" value )* "}"
+
+struct_type_spec        : "struct" struct_body
+
+struct_body             : "{" ( declaration ";" )+ "}"
+
+union_type_spec         : "union" union_body
+
+union_body              : switch_spec "{" case_spec+ [ default_spec ] "}"
+
+switch_spec             : "switch" "(" declaration ")"
+
+case_spec               : ( "case" value ":" )+ declaration ";"
+
+default_spec            : "default" ":" declaration ";"
+
+constant_def            : "const" identifier "=" value ";"
+
+type_def                : "typedef" declaration ";"                -> typedef
+                        | "enum" identifier enum_body ";"          -> enum
+                        | "struct" identifier struct_body ";"      -> struct
+                        | "union" identifier union_body ";"        -> union
+
+specification           : definition*
+
+definition              : constant_def
+                        | type_def
+                        | program_def
+                        | pragma_def
+
+//
+// RPC program definitions not specified in RFC 4506
+//
+
+program_def             : "program" identifier "{" version_def+ "}" "=" constant ";"
+
+version_def             : "version" identifier "{" procedure_def+ "}" "=" constant ";"
+
+procedure_def           : type_specifier identifier "(" type_specifier ")" "=" constant ";"
+
+pragma_def              : "pragma" directive identifier [ identifier ] ";"
+
+directive               : header_directive
+                        | pages_directive
+			| public_directive
+			| skip_directive
+
+header_directive        : "header"
+pages_directive         : "pages"
+public_directive        : "public"
+skip_directive          : "skip"
+
+//
+// XDR language primitives
+//
+
+identifier              : /([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])*/
+
+decimal_constant        : /[\+-]?(0|[1-9][0-9]*)/
+hexadecimal_constant    : /0x([a-f]|[A-F]|[0-9])+/
+octal_constant          : /0[0-7]+/
+
+COMMENT                 : "/*" /(.|\n)*?/ "*/"
+%ignore COMMENT
+
+PASSTHRU                : "%" | "%" /.+/
+%ignore PASSTHRU
+
+WS                      : /[ \t\f\r\n]/+
+%ignore WS
diff --git a/tools/net/sunrpc/xdrgen/xdr_ast.py b/tools/net/sunrpc/xdrgen/xdr_ast.py
new file mode 100644
index 000000000000..1592995cfda3
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/xdr_ast.py
@@ -0,0 +1,437 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Define and implement the Abstract Syntax Tree for the XDR language."""
+
+import sys
+from typing import List
+from dataclasses import dataclass
+
+from lark import ast_utils, Transformer
+from lark.tree import Meta
+
+this_module = sys.modules[__name__]
+
+header_name = "none"
+public_apis = []
+enums = set()
+structs = set()
+
+
+@dataclass
+class _Ast(ast_utils.Ast):
+    """Parent class for the XDR abstract syntax tree"""
+
+
+@dataclass
+class _XdrConstant(_Ast):
+    """Corresponds to 'constant_def' in the grammar"""
+
+    name: str
+    value: str
+
+
+@dataclass
+class _XdrEnumerator(_Ast):
+    """An 'identifier = value' enumerator"""
+
+    name: str
+    value: str
+
+
+@dataclass
+class _XdrEnum(_Ast):
+    """An XDR enum definition"""
+
+    name: str
+    minimum: int
+    maximum: int
+    enumerators: List[_XdrEnumerator]
+
+
+@dataclass
+class _TypeSpecifier(_Ast):
+    """Corresponds to 'type_specifier' in the grammar"""
+
+    type_name: str
+
+
+@dataclass
+class _DefinedType(_TypeSpecifier):
+    type_decorator: str
+    pass_by_reference: bool
+
+
+@dataclass
+class _BuiltInType(_TypeSpecifier):
+    display_name: str
+    type_decorator: str = ""
+    pass_by_reference: bool = False
+
+
+@dataclass
+class _Declaration(_Ast):
+    """Parent class of type declarations"""
+
+
+@dataclass
+class _FixedLengthOpaque(_Declaration):
+    """A fixed-length opaque declaration"""
+
+    name: str
+    size: str
+    template: str = "fixed_length_opaque"
+
+
+@dataclass
+class _VariableLengthOpaque(_Declaration):
+    """A variable-length opaque declaration"""
+
+    name: str
+    maxsize: str
+    template: str = "variable_length_opaque"
+
+
+@dataclass
+class _VariableLengthString(_Declaration):
+    """A variable-length string declaration"""
+
+    name: str
+    maxsize: str
+    template: str = "variable_length_string"
+
+
+@dataclass
+class _FixedLengthArray(_Declaration):
+    """A fixed-length array declaration"""
+
+    name: str
+    spec: _TypeSpecifier
+    size: str
+    template: str = "fixed_length_array"
+
+
+@dataclass
+class _VariableLengthArray(_Declaration):
+    """A variable-length array declaration"""
+
+    name: str
+    spec: _TypeSpecifier
+    maxsize: str
+    template: str = "variable_length_array"
+
+
+@dataclass
+class _XdrOptionalData(_Declaration):
+    """An 'optional_data' declaration"""
+
+    name: str
+    spec: _TypeSpecifier
+    template: str = "optional_data"
+
+
+@dataclass
+class _BasicDeclaration(_Declaration):
+    """A 'basic' declaration"""
+
+    name: str
+    spec: _TypeSpecifier
+    template: str = "basic"
+
+
+@dataclass
+class _XdrVoid(_Declaration):
+    """A void declaration"""
+
+
+@dataclass
+class _XdrStruct(_Ast):
+    """An XDR struct definition"""
+
+    name: str
+    fields: List[_Declaration]
+
+
+@dataclass
+class _XdrTypedef(_Ast):
+    """An XDR typedef"""
+
+    declaration: _Declaration
+
+
+@dataclass
+class _CaseSpec(_Ast):
+    """One case in an XDR union"""
+
+    values: List[str]
+    arm: _Declaration
+    template: str = "case_spec"
+
+
+@dataclass
+class _DefaultSpec(_Ast):
+    """Default case in an XDR union"""
+
+    arm: _Declaration
+    template: str = "default_spec"
+
+
+@dataclass
+class _XdrUnion(_Ast):
+    """An XDR union"""
+
+    name: str
+    discriminant: _Declaration
+    cases: List[_CaseSpec]
+    default: _Declaration
+
+
+@dataclass
+class _RpcProcedure(_Ast):
+    """RPC procedure definition"""
+
+    name: str
+    number: str
+    argument: _TypeSpecifier
+    result: _TypeSpecifier
+
+
+@dataclass
+class _RpcVersion(_Ast):
+    """RPC version definition"""
+
+    name: str
+    number: str
+    procedures: List[_RpcProcedure]
+
+
+@dataclass
+class _RpcProgram(_Ast):
+    """RPC program definition"""
+
+    name: str
+    number: str
+    versions: List[_RpcVersion]
+
+
+@dataclass
+class _Pragma(_Ast):
+    """Empty class for pragma directives"""
+
+
+@dataclass
+class Definition(_Ast, ast_utils.WithMeta):
+    """Corresponds to 'definition' in the grammar"""
+
+    meta: Meta
+    value: object
+
+
+@dataclass
+class Specification(_Ast, ast_utils.AsList):
+    """Corresponds to 'specification' in the grammar"""
+
+    definitions: List[Definition]
+
+
+class ParseToAst(Transformer):
+    """Functions that transform productions into AST nodes"""
+
+    def constant_def(self, children):
+        """Instantiate one _XdrConstant object"""
+        name = children[0].children[0].value
+        value = children[1].children[0].children[0].value
+        return _XdrConstant(name, value)
+
+    # cel: Python can compute a min() and max() for the enumerator values
+    #      so that the generated code can perform proper range checking.
+    def enum(self, children):
+        """Instantiate one _XdrEnum object"""
+        enum_name = children[0].children[0].value
+        enums.add(enum_name)
+
+        i = 0
+        enumerators = []
+        body = children[1]
+        while i < len(body.children):
+            name = body.children[i].children[0].value
+            value = body.children[i + 1].children[0].children[0].value
+            enumerators.append(_XdrEnumerator(name, value))
+            i = i + 2
+
+        return _XdrEnum(enum_name, 0, 0, enumerators)
+
+    def type_specifier(self, children):
+        """Instantiate one type_specifier object"""
+        if children[0].data == "identifier":
+            name = children[0].children[0].value
+            decorator = ""
+            pbr = False
+            if name in enums:
+                decorator = "enum "
+            if name in structs:
+                decorator = "struct "
+                pbr = True
+            return _DefinedType(name, decorator, pbr)
+
+        token = children[0].data
+        return _BuiltInType(
+            type_name=token.value, display_name=token.value.replace("_", " ")
+        )
+
+    def fixed_length_opaque(self, children):
+        """Instantiate one _FixedLengthOpaque declaration object"""
+        name = children[0].children[0].value
+        size = children[1].children[0].children[0].value
+
+        return _FixedLengthOpaque(name, size)
+
+    def variable_length_opaque(self, children):
+        """Instantiate one _VariableLengthOpaque declaration object"""
+        name = children[0].children[0].value
+        if children[1] is not None:
+            maxsize = children[1].children[0].children[0].value
+        else:
+            maxsize = 0
+
+        return _VariableLengthOpaque(name, maxsize)
+
+    def variable_length_string(self, children):
+        """Instantiate one _VariableLengthString declaration object"""
+        name = children[0].children[0].value
+        if children[1] is not None:
+            maxsize = children[1].children[0].children[0].value
+        else:
+            maxsize = 0
+
+        return _VariableLengthString(name, maxsize)
+
+    def fixed_length_array(self, children):
+        """Instantiate one _FixedLengthArray declaration object"""
+        spec = children[0]
+        name = children[1].children[0].value
+        size = children[2].children[0].children[0].value
+
+        return _FixedLengthArray(name, spec, size)
+
+    def variable_length_array(self, children):
+        """Instantiate one _VariableLengthArray declaration object"""
+        spec = children[0]
+        name = children[1].children[0].value
+        if children[2] is not None:
+            maxsize = children[2].children[0].children[0].value
+        else:
+            maxsize = 0
+
+        return _VariableLengthArray(name, spec, maxsize)
+
+    def optional_data(self, children):
+        """Instantiate one _XdrOptionalData declaration object"""
+        spec = children[0]
+        name = children[1].children[0].value
+        structs.add(name)
+
+        return _XdrOptionalData(name, spec)
+
+    def basic(self, children):
+        """Instantiate one _BasicDeclaration object"""
+        spec = children[0]
+        name = children[1].children[0].value
+
+        return _BasicDeclaration(name, spec)
+
+    def void(self, children):
+        """Instantiate one _XdrVoid declaration object"""
+        return _XdrVoid()
+
+    def struct(self, children):
+        """Instantiate one _XdrStruct object"""
+        name = children[0].children[0].value
+        structs.add(name)
+        fields = children[1].children
+
+        return _XdrStruct(name, fields)
+
+    def typedef(self, children):
+        """Instantiate one _XdrTypedef object"""
+
+        return _XdrTypedef(children[0])
+
+    def case_spec(self, children):
+        """Instantiate one _CaseSpec object"""
+        values = []
+        for item in children[0:-1]:
+            value = item.children[0].children[0].value
+            values.append(value)
+        arm = children[-1]
+
+        return _CaseSpec(values, arm)
+
+    def default_spec(self, children):
+        """Instantiate one _DefaultSpec object"""
+        arm = children[0]
+
+        return _DefaultSpec(arm)
+
+    def union(self, children):
+        """Instantiate one _XdrUnion object"""
+        name = children[0].children[0].value
+        structs.add(name)
+        body = children[1]
+        discriminant = body.children[0].children[0]
+        cases = body.children[1:-1]
+        default = body.children[-1]
+
+        return _XdrUnion(name, discriminant, cases, default)
+
+    def procedure_def(self, children):
+        """Instantiate one _RpcProcedure object"""
+        result = children[0]
+        name = children[1].children[0].value
+        argument = children[2]
+        number = children[3].children[0].children[0].value
+
+        return _RpcProcedure(name, number, argument, result)
+
+    def version_def(self, children):
+        """Instantiate one _RpcVersion object"""
+        name = children[0].children[0].value
+        number = children[-1].children[0].children[0].value
+        procedures = children[1:-1]
+
+        return _RpcVersion(name, number, procedures)
+
+    def program_def(self, children):
+        """Instantiate one _RpcProgram object"""
+        name = children[0].children[0].value
+        number = children[-1].children[0].children[0].value
+        versions = children[1:-1]
+
+        return _RpcProgram(name, number, versions)
+
+    def pragma_def(self, children):
+        """Instantiate one _Pragma object"""
+        directive = children[0].children[0].data
+        match directive:
+            case "public_directive":
+                public_apis.append(children[1].children[0].value)
+            case "header_directive":
+                global header_name
+                header_name = children[1].children[0].value
+            case _:
+                raise NotImplementedError("Directive not supported")
+        return _Pragma()
+
+
+transformer = ast_utils.create_transformer(this_module, ParseToAst())
+
+
+def transform_parse_tree(parse_tree):
+    """Transform productions into an abstract syntax tree"""
+
+    return transformer.transform(parse_tree)
+
+
+def get_header_name():
+    """Return header name set by pragma header directive"""
+    return header_name
diff --git a/tools/net/sunrpc/xdrgen/xdr_parse.py b/tools/net/sunrpc/xdrgen/xdr_parse.py
new file mode 100644
index 000000000000..0631aa6f5c8c
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/xdr_parse.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Common parsing code for xdrgen"""
+
+from lark import Lark, Discard
+
+
+def xdr_parser():
+    """Return a Lark parser instance configured with the XDR language grammar"""
+
+    return Lark.open(
+        "xdr.ebnf",
+        start="specification",
+        debug=True,
+        strict=True,
+        propagate_positions=True,
+        parser="lalr",
+        lexer="contextual",
+    )
diff --git a/tools/net/sunrpc/xdrgen/xdrgen b/tools/net/sunrpc/xdrgen/xdrgen
new file mode 100755
index 000000000000..6c44a2823abe
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/xdrgen
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+__author__ = "Chuck Lever"
+__copyright__ = "Copyright (c) 2024 Oracle and/or its affiliates."
+__license__ = "GPL-2.0 only"
+__version__ = "0.2"
+
+import sys
+import argparse
+
+from subcmds import source
+from subcmds import header
+from subcmds import lint
+
+from common import set_xdr_annotate
+
+
+sys.path.insert(1, "@pythondir@")
+
+
+def main():
+    """Parse command-line options"""
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description="Convert an XDR specification to Linux kernel source code",
+        epilog="""\
+Copyright (c) 2024 Oracle and/or its affiliates.
+
+License GPLv2: <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>
+This is free software.  You are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.""",
+    )
+    parser.add_argument(
+        "--version",
+        help="Display the version of this tool",
+        action="version",
+        version=__version__,
+    )
+
+    subcommands = parser.add_subparsers(title="Subcommands", required=True)
+
+    linter = subcommands.add_parser("lint", help="Check an XDR specification")
+    linter.add_argument("filename", help="File containing an XDR specification")
+    linter.set_defaults(func=lint.subcmd)
+
+    source_output = subcommands.add_parser(
+        "source", help="Generate XDR encoder and decoder source code"
+    )
+    source_output.add_argument(
+        "--annotate",
+        action="store_true",
+        default=False,
+        help="Add annotation comments",
+    )
+    source_output.add_argument(
+        "--language",
+        action="store_true",
+        default="C",
+        help="Output language",
+    )
+    source_output.add_argument("filename", help="File containing an XDR specification")
+    source_output.set_defaults(func=source.subcmd)
+
+    header_output = subcommands.add_parser(
+        "header", help="Generate XDR definitions and declarations"
+    )
+    header_output.add_argument(
+        "--annotate",
+        action="store_true",
+        default=False,
+        help="Add annotation comments",
+    )
+    header_output.add_argument(
+        "--language",
+        action="store_true",
+        default="C",
+        help="Output language",
+    )
+    header_output.add_argument("filename", help="File containing an XDR specification")
+    header_output.set_defaults(func=header.subcmd)
+
+    args = parser.parse_args()
+    set_xdr_annotate(args.annotate)
+    return args.func(args)
+
+
+try:
+    if __name__ == "__main__":
+        sys.exit(main())
+except (SystemExit, KeyboardInterrupt, RuntimeError):
+    sys.exit(1)
-- 
2.45.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-20 14:45 [RFC PATCH 0/2] xdrgen - machine-generated XDR functions cel
  2024-08-20 14:45 ` [RFC PATCH 1/2] tools: Add xdrgen cel
@ 2024-08-20 14:46 ` cel
  2024-08-21 14:22   ` Jeff Layton
  2024-08-22 16:34   ` Jeff Layton
  2024-08-21 14:03 ` [RFC PATCH 0/2] xdrgen - machine-generated XDR functions Jeff Layton
  2 siblings, 2 replies; 16+ messages in thread
From: cel @ 2024-08-20 14:46 UTC (permalink / raw)
  To: linux-nfs; +Cc: Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

Build an NFSv4 protocol snippet to support the delstid extensions.
The new fs/nfsd/nfs4_1.x file can be added to over time as other
parts of NFSD's XDR functions are converted to machine-generated
code.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
 fs/nfsd/nfs4xdr_gen.c | 236 ++++++++++++++++++++++++++++++++++++++++++
 fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
 3 files changed, 513 insertions(+)
 create mode 100644 fs/nfsd/nfs4_1.x
 create mode 100644 fs/nfsd/nfs4xdr_gen.c
 create mode 100644 fs/nfsd/nfs4xdr_gen.h

diff --git a/fs/nfsd/nfs4_1.x b/fs/nfsd/nfs4_1.x
new file mode 100644
index 000000000000..d2fde450de5e
--- /dev/null
+++ b/fs/nfsd/nfs4_1.x
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2010 IETF Trust and the persons identified
+ * as the document authors.  All rights reserved.
+ *
+ * The document authors are identified in RFC 3530 and
+ * RFC 5661.
+ *
+ * Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ *   copyright notice, this list of conditions and the
+ *   following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the
+ *   following disclaimer in the documentation and/or other
+ *   materials provided with the distribution.
+ *
+ * - Neither the name of Internet Society, IETF or IETF
+ *   Trust, nor the names of specific contributors, may be
+ *   used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
+ *   AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ *   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+ *   EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ *   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ *   IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+pragma header nfs4;
+
+/*
+ * Basic typedefs for RFC 1832 data type definitions
+ */
+typedef hyper		int64_t;
+typedef unsigned int	uint32_t;
+
+/*
+ * Basic data types
+ */
+typedef uint32_t	bitmap4<>;
+
+/*
+ * Timeval
+ */
+struct nfstime4 {
+	int64_t		seconds;
+	uint32_t	nseconds;
+};
+
+
+/*
+ * The following content was extracted from draft-ietf-nfsv4-delstid
+ */
+
+typedef bool            fattr4_offline;
+
+
+const FATTR4_OFFLINE            = 83;
+
+
+struct open_arguments4 {
+  bitmap4  oa_share_access;
+  bitmap4  oa_share_deny;
+  bitmap4  oa_share_access_want;
+  bitmap4  oa_open_claim;
+  bitmap4  oa_create_mode;
+};
+
+
+enum open_args_share_access4 {
+   OPEN_ARGS_SHARE_ACCESS_READ  = 1,
+   OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
+   OPEN_ARGS_SHARE_ACCESS_BOTH  = 3
+};
+
+
+enum open_args_share_deny4 {
+   OPEN_ARGS_SHARE_DENY_NONE  = 0,
+   OPEN_ARGS_SHARE_DENY_READ  = 1,
+   OPEN_ARGS_SHARE_DENY_WRITE = 2,
+   OPEN_ARGS_SHARE_DENY_BOTH  = 3
+};
+
+
+enum open_args_share_access_want4 {
+   OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG           = 3,
+   OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG            = 4,
+   OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL              = 5,
+   OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL
+                                                   = 17,
+   OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED
+                                                   = 18,
+   OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS    = 20,
+   OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21
+};
+
+
+enum open_args_open_claim4 {
+   OPEN_ARGS_OPEN_CLAIM_NULL          = 0,
+   OPEN_ARGS_OPEN_CLAIM_PREVIOUS      = 1,
+   OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR  = 2,
+   OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
+   OPEN_ARGS_OPEN_CLAIM_FH            = 4,
+   OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH  = 5,
+   OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6
+};
+
+
+enum open_args_createmode4 {
+   OPEN_ARGS_CREATEMODE_UNCHECKED4     = 0,
+   OPEN_ARGS_CREATE_MODE_GUARDED       = 1,
+   OPEN_ARGS_CREATEMODE_EXCLUSIVE4     = 2,
+   OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1  = 3
+};
+
+
+typedef open_arguments4 fattr4_open_arguments;
+pragma public fattr4_open_arguments;
+
+
+%/*
+% * Determine what OPEN supports.
+% */
+const FATTR4_OPEN_ARGUMENTS     = 86;
+
+
+const OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000;
+
+
+const OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010;
+
+
+/*
+ * attributes for the delegation times being
+ * cached and served by the "client"
+ */
+typedef nfstime4        fattr4_time_deleg_access;
+typedef nfstime4        fattr4_time_deleg_modify;
+
+
+%/*
+% * New RECOMMENDED Attribute for
+% * delegation caching of times
+% */
+const FATTR4_TIME_DELEG_ACCESS  = 84;
+const FATTR4_TIME_DELEG_MODIFY  = 85;
+
+
+const OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000;
+
diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
new file mode 100644
index 000000000000..bb714859d6c6
--- /dev/null
+++ b/fs/nfsd/nfs4xdr_gen.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+// Generated by xdrgen. Manual edits will be lost.
+// XDR specification modification time: Mon Aug 19 23:28:23 2024
+
+#include "nfs4xdr_gen.h"
+
+static bool __maybe_unused
+xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
+{
+	return xdrgen_decode_hyper(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
+{
+	return xdrgen_decode_unsigned_int(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
+{
+	if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
+		return false;
+	for (u32 i = 0; i < ptr->count; i++)
+		if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i]))
+			return false;
+	return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
+{
+	if (!xdrgen_decode_int64_t(xdr, &ptr->seconds))
+		return false;
+	if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds))
+		return false;
+	return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
+{
+	return xdrgen_decode_bool(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr)
+{
+	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access))
+		return false;
+	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_deny))
+		return false;
+	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access_want))
+		return false;
+	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_open_claim))
+		return false;
+	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode))
+		return false;
+	return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 *ptr)
+{
+	u32 val;
+
+	if (xdr_stream_decode_u32(xdr, &val) < 0)
+		return false;
+	*ptr = val;
+	return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 *ptr)
+{
+	u32 val;
+
+	if (xdr_stream_decode_u32(xdr, &val) < 0)
+		return false;
+	*ptr = val;
+	return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 *ptr)
+{
+	u32 val;
+
+	if (xdr_stream_decode_u32(xdr, &val) < 0)
+		return false;
+	*ptr = val;
+	return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 *ptr)
+{
+	u32 val;
+
+	if (xdr_stream_decode_u32(xdr, &val) < 0)
+		return false;
+	*ptr = val;
+	return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 *ptr)
+{
+	u32 val;
+
+	if (xdr_stream_decode_u32(xdr, &val) < 0)
+		return false;
+	*ptr = val;
+	return true;
+}
+
+bool
+xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr)
+{
+	return xdrgen_decode_open_arguments4(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
+{
+	return xdrgen_decode_nfstime4(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr)
+{
+	return xdrgen_decode_nfstime4(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
+{
+	return xdrgen_encode_hyper(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
+{
+	return xdrgen_encode_unsigned_int(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
+{
+	if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
+		return false;
+	for (u32 i = 0; i < value.count; i++)
+		if (!xdrgen_encode_uint32_t(xdr, value.element[i]))
+			return false;
+	return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
+{
+	if (!xdrgen_encode_int64_t(xdr, value->seconds))
+		return false;
+	if (!xdrgen_encode_uint32_t(xdr, value->nseconds))
+		return false;
+	return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
+{
+	return xdrgen_encode_bool(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value)
+{
+	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access))
+		return false;
+	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_deny))
+		return false;
+	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access_want))
+		return false;
+	if (!xdrgen_encode_bitmap4(xdr, value->oa_open_claim))
+		return false;
+	if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode))
+		return false;
+	return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 value)
+{
+	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 value)
+{
+	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 value)
+{
+	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 value)
+{
+	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 value)
+{
+	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+bool
+xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value)
+{
+	return xdrgen_encode_open_arguments4(xdr, &value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access value)
+{
+	return xdrgen_encode_nfstime4(xdr, &value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify value)
+{
+	return xdrgen_encode_nfstime4(xdr, &value);
+};
diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
new file mode 100644
index 000000000000..27c601b36580
--- /dev/null
+++ b/fs/nfsd/nfs4xdr_gen.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Generated by xdrgen. Manual edits will be lost. */
+/* XDR specification modification time: Mon Aug 19 23:28:23 2024 */
+
+#ifndef _LINUX_NFS4_XDRGEN_H
+#define _LINUX_NFS4_XDRGEN_H
+
+#include <linux/types.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/sunrpc/xdrgen-builtins.h>
+
+typedef s64 int64_t;
+
+typedef u32 uint32_t;
+
+typedef struct {
+	u32 count;
+	uint32_t *element;
+} bitmap4;
+
+struct nfstime4 {
+	int64_t seconds;
+	uint32_t nseconds;
+};
+
+typedef bool fattr4_offline;
+
+enum {
+	FATTR4_OFFLINE = 83
+};
+
+struct open_arguments4 {
+	bitmap4 oa_share_access;
+	bitmap4 oa_share_deny;
+	bitmap4 oa_share_access_want;
+	bitmap4 oa_open_claim;
+	bitmap4 oa_create_mode;
+};
+
+enum open_args_share_access4 {
+	OPEN_ARGS_SHARE_ACCESS_READ = 1,
+	OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
+	OPEN_ARGS_SHARE_ACCESS_BOTH = 3,
+};
+
+enum open_args_share_deny4 {
+	OPEN_ARGS_SHARE_DENY_NONE = 0,
+	OPEN_ARGS_SHARE_DENY_READ = 1,
+	OPEN_ARGS_SHARE_DENY_WRITE = 2,
+	OPEN_ARGS_SHARE_DENY_BOTH = 3,
+};
+
+enum open_args_share_access_want4 {
+	OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG = 3,
+	OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG = 4,
+	OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL = 5,
+	OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 17,
+	OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 18,
+	OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20,
+	OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21,
+};
+
+enum open_args_open_claim4 {
+	OPEN_ARGS_OPEN_CLAIM_NULL = 0,
+	OPEN_ARGS_OPEN_CLAIM_PREVIOUS = 1,
+	OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR = 2,
+	OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
+	OPEN_ARGS_OPEN_CLAIM_FH = 4,
+	OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5,
+	OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6,
+};
+
+enum open_args_createmode4 {
+	OPEN_ARGS_CREATEMODE_UNCHECKED4 = 0,
+	OPEN_ARGS_CREATE_MODE_GUARDED = 1,
+	OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2,
+	OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3,
+};
+
+typedef struct open_arguments4 fattr4_open_arguments;
+bool xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr);
+bool xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value);
+
+enum {
+	FATTR4_OPEN_ARGUMENTS = 86
+};
+
+enum {
+	OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000
+};
+
+enum {
+	OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010
+};
+
+typedef struct nfstime4 fattr4_time_deleg_access;
+
+typedef struct nfstime4 fattr4_time_deleg_modify;
+
+enum {
+	FATTR4_TIME_DELEG_ACCESS = 84
+};
+
+enum {
+	FATTR4_TIME_DELEG_MODIFY = 85
+};
+
+enum {
+	OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000
+};
+
+#endif /* _LINUX_NFS4_XDRGEN_H */
-- 
2.45.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/2] xdrgen - machine-generated XDR functions
  2024-08-20 14:45 [RFC PATCH 0/2] xdrgen - machine-generated XDR functions cel
  2024-08-20 14:45 ` [RFC PATCH 1/2] tools: Add xdrgen cel
  2024-08-20 14:46 ` [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file cel
@ 2024-08-21 14:03 ` Jeff Layton
  2024-08-21 14:06   ` Chuck Lever III
  2 siblings, 1 reply; 16+ messages in thread
From: Jeff Layton @ 2024-08-21 14:03 UTC (permalink / raw)
  To: cel, linux-nfs; +Cc: Chuck Lever

On Tue, 2024-08-20 at 10:45 -0400, cel@kernel.org wrote:
> From: Chuck Lever <chuck.lever@oracle.com>
> 
> For a description of this work-in-progress, see the new README file.
> For unfinished work items, ditto.
> 
> The first patch adds the tool.
> 
> The second patch is an nfs4_1.x file that I created to confirm that
> the tool indeed works for Jeff's purposes. Actual generated header
> and source are in fs/nfsd/nfs4xdr_gen.? .
> 
> Feel free to ignore 2/2. It is included here only to demonstrate the
> tool's new "pragma" directives that I think can avoid the need to
> hand-edit generated source, as discussed in an earlier thread. Grep
> the new README or nfs4_1.x for "pragma".
> 
> These patches apply to v6.11-rc4, but can be rebased on nfsd-next if
> they are found merge-worthy. See also:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git/log/?h=lkxdrgen
> 
> For review, note that the numerous .j2 files are all tiny and can be
> skimmed in favor of the .py files, where the tool's logic is.
> 
> All Python-related advice and guidance is gratefully accepted.
> 
> --
> 
> Chuck Lever (2):
>   tools: Add xdrgen
>   NFSD: Create an initial nfs4_1.x file
> 
>  fs/nfsd/nfs4_1.x                              | 164 +++++++
>  fs/nfsd/nfs4xdr_gen.c                         | 236 ++++++++++
>  fs/nfsd/nfs4xdr_gen.h                         | 113 +++++
>  include/linux/sunrpc/xdrgen-builtins.h        | 253 ++++++++++
>  tools/net/sunrpc/xdrgen/.gitignore            |   2 +
>  tools/net/sunrpc/xdrgen/README                | 231 +++++++++
>  tools/net/sunrpc/xdrgen/__init__.py           |   0
>  tools/net/sunrpc/xdrgen/common.py             |  21 +
>  .../net/sunrpc/xdrgen/generators/__init__.py  |   0
>  .../sunrpc/xdrgen/generators/boilerplate.py   |  66 +++
>  .../net/sunrpc/xdrgen/generators/constant.py  |  38 ++
>  tools/net/sunrpc/xdrgen/generators/enum.py    |  67 +++
>  tools/net/sunrpc/xdrgen/generators/program.py |  96 ++++
>  tools/net/sunrpc/xdrgen/generators/struct.py  | 435 +++++++++++++++++
>  tools/net/sunrpc/xdrgen/generators/typedef.py | 282 +++++++++++
>  tools/net/sunrpc/xdrgen/generators/union.py   | 297 ++++++++++++
>  tools/net/sunrpc/xdrgen/subcmds/__init__.py   |   0
>  tools/net/sunrpc/xdrgen/subcmds/header.py     |  62 +++
>  tools/net/sunrpc/xdrgen/subcmds/lint.py       |  28 ++
>  tools/net/sunrpc/xdrgen/subcmds/source.py     |  82 ++++
>  .../templates/C/boilerplate/header_bottom.j2  |   3 +
>  .../templates/C/boilerplate/header_top.j2     |  11 +
>  .../templates/C/boilerplate/source_top.j2     |   5 +
>  .../templates/C/constants/declaration.j2      |   5 +
>  .../templates/C/enum/declaration/close.j2     |   7 +
>  .../C/enum/declaration/enumerator.j2          |   2 +
>  .../templates/C/enum/declaration/open.j2      |   3 +
>  .../xdrgen/templates/C/enum/decoder/enum.j2   |  19 +
>  .../xdrgen/templates/C/enum/encoder/enum.j2   |  14 +
>  .../C/program/declaration/argument.j2         |   2 +
>  .../templates/C/program/declaration/result.j2 |   2 +
>  .../templates/C/program/decoder/argument.j2   |  21 +
>  .../templates/C/program/encoder/result.j2     |  21 +
>  .../templates/C/struct/declaration/basic.j2   |   5 +
>  .../templates/C/struct/declaration/close.j2   |   7 +
>  .../struct/declaration/fixed_length_array.j2  |   5 +
>  .../struct/declaration/fixed_length_opaque.j2 |   5 +
>  .../templates/C/struct/declaration/open.j2    |   6 +
>  .../C/struct/declaration/optional_data.j2     |   5 +
>  .../C/struct/declaration/pointer-open.j2      |   6 +
>  .../declaration/variable_length_array.j2      |   8 +
>  .../declaration/variable_length_opaque.j2     |   5 +
>  .../declaration/variable_length_string.j2     |   5 +
>  .../templates/C/struct/decoder/basic.j2       |   6 +
>  .../templates/C/struct/decoder/close.j2       |   3 +
>  .../C/struct/decoder/fixed_length_array.j2    |   8 +
>  .../C/struct/decoder/fixed_length_opaque.j2   |   6 +
>  .../xdrgen/templates/C/struct/decoder/open.j2 |  12 +
>  .../C/struct/decoder/optional_data.j2         |   6 +
>  .../C/struct/decoder/pointer-open.j2          |  22 +
>  .../C/struct/decoder/variable_length_array.j2 |  13 +
>  .../struct/decoder/variable_length_opaque.j2  |   6 +
>  .../struct/decoder/variable_length_string.j2  |   6 +
>  .../templates/C/struct/encoder/basic.j2       |  10 +
>  .../templates/C/struct/encoder/close.j2       |   3 +
>  .../C/struct/encoder/fixed_length_array.j2    |  12 +
>  .../C/struct/encoder/fixed_length_opaque.j2   |   6 +
>  .../xdrgen/templates/C/struct/encoder/open.j2 |  12 +
>  .../C/struct/encoder/optional_data.j2         |   6 +
>  .../C/struct/encoder/pointer-open.j2          |  20 +
>  .../C/struct/encoder/variable_length_array.j2 |  15 +
>  .../struct/encoder/variable_length_opaque.j2  |   8 +
>  .../struct/encoder/variable_length_string.j2  |   8 +
>  .../templates/C/typedef/declaration/basic.j2  |  11 +
>  .../typedef/declaration/fixed_length_array.j2 |  11 +
>  .../declaration/fixed_length_opaque.j2        |  11 +
>  .../declaration/variable_length_array.j2      |  14 +
>  .../declaration/variable_length_opaque.j2     |  11 +
>  .../declaration/variable_length_string.j2     |  11 +
>  .../templates/C/typedef/decoder/basic.j2      |  17 +
>  .../C/typedef/decoder/fixed_length_array.j2   |  25 +
>  .../C/typedef/decoder/fixed_length_opaque.j2  |  17 +
>  .../typedef/decoder/variable_length_array.j2  |  26 ++
>  .../typedef/decoder/variable_length_opaque.j2 |  17 +
>  .../typedef/decoder/variable_length_string.j2 |  17 +
>  .../templates/C/typedef/encoder/basic.j2      |  21 +
>  .../C/typedef/encoder/fixed_length_array.j2   |  25 +
>  .../C/typedef/encoder/fixed_length_opaque.j2  |  17 +
>  .../typedef/encoder/variable_length_array.j2  |  30 ++
>  .../typedef/encoder/variable_length_opaque.j2 |  17 +
>  .../typedef/encoder/variable_length_string.j2 |  17 +
>  .../C/union/declaration/case_spec.j2          |   2 +
>  .../templates/C/union/declaration/close.j2    |   8 +
>  .../C/union/declaration/default_spec.j2       |   2 +
>  .../templates/C/union/declaration/open.j2     |   6 +
>  .../C/union/declaration/switch_spec.j2        |   3 +
>  .../xdrgen/templates/C/union/decoder/basic.j2 |   6 +
>  .../xdrgen/templates/C/union/decoder/break.j2 |   2 +
>  .../templates/C/union/decoder/case_spec.j2    |   2 +
>  .../xdrgen/templates/C/union/decoder/close.j2 |   4 +
>  .../templates/C/union/decoder/default_spec.j2 |   2 +
>  .../xdrgen/templates/C/union/decoder/open.j2  |  12 +
>  .../C/union/decoder/optional_data.j2          |   6 +
>  .../templates/C/union/decoder/switch_spec.j2  |   7 +
>  .../C/union/decoder/variable_length_array.j2  |  13 +
>  .../C/union/decoder/variable_length_opaque.j2 |   6 +
>  .../C/union/decoder/variable_length_string.j2 |   6 +
>  .../xdrgen/templates/C/union/decoder/void.j2  |   3 +
>  .../xdrgen/templates/C/union/encoder/basic.j2 |  10 +
>  .../xdrgen/templates/C/union/encoder/break.j2 |   2 +
>  .../templates/C/union/encoder/case_spec.j2    |   2 +
>  .../xdrgen/templates/C/union/encoder/close.j2 |   4 +
>  .../templates/C/union/encoder/default_spec.j2 |   2 +
>  .../xdrgen/templates/C/union/encoder/open.j2  |  12 +
>  .../templates/C/union/encoder/switch_spec.j2  |   7 +
>  .../xdrgen/templates/C/union/encoder/void.j2  |   3 +
>  tools/net/sunrpc/xdrgen/tests/test.x          |  36 ++
>  tools/net/sunrpc/xdrgen/xdr                   |   1 +
>  tools/net/sunrpc/xdrgen/xdr.ebnf              | 117 +++++
>  tools/net/sunrpc/xdrgen/xdr_ast.py            | 437 ++++++++++++++++++
>  tools/net/sunrpc/xdrgen/xdr_parse.py          |  20 +
>  tools/net/sunrpc/xdrgen/xdrgen                |  95 ++++
>  112 files changed, 3986 insertions(+)
>  create mode 100644 fs/nfsd/nfs4_1.x
>  create mode 100644 fs/nfsd/nfs4xdr_gen.c
>  create mode 100644 fs/nfsd/nfs4xdr_gen.h
>  create mode 100644 include/linux/sunrpc/xdrgen-builtins.h
>  create mode 100644 tools/net/sunrpc/xdrgen/.gitignore
>  create mode 100644 tools/net/sunrpc/xdrgen/README
>  create mode 100644 tools/net/sunrpc/xdrgen/__init__.py
>  create mode 100644 tools/net/sunrpc/xdrgen/common.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/__init__.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/boilerplate.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/constant.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/enum.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/program.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/struct.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/typedef.py
>  create mode 100644 tools/net/sunrpc/xdrgen/generators/union.py
>  create mode 100644 tools/net/sunrpc/xdrgen/subcmds/__init__.py
>  create mode 100755 tools/net/sunrpc/xdrgen/subcmds/header.py
>  create mode 100755 tools/net/sunrpc/xdrgen/subcmds/lint.py
>  create mode 100755 tools/net/sunrpc/xdrgen/subcmds/source.py
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_bottom.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_top.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/source_top.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/constants/declaration.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enumerator.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/optional_data.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/pointer-open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_string.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/pointer-open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/pointer-open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/case_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/default_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/switch_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2
>  create mode 100644 tools/net/sunrpc/xdrgen/tests/test.x
>  create mode 120000 tools/net/sunrpc/xdrgen/xdr
>  create mode 100644 tools/net/sunrpc/xdrgen/xdr.ebnf
>  create mode 100644 tools/net/sunrpc/xdrgen/xdr_ast.py
>  create mode 100644 tools/net/sunrpc/xdrgen/xdr_parse.py
>  create mode 100755 tools/net/sunrpc/xdrgen/xdrgen
> 

This all looks great to me. I really support autogenerating our XDR
boilerplate, particularly as I've been looking at implementing
CB_NOTIFY (which would be quite tedious to roll by hand). This would
make it a lot simpler and less error prone.

Do you have these patches in a branch in your tree that I could pick
from?
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/2] xdrgen - machine-generated XDR functions
  2024-08-21 14:03 ` [RFC PATCH 0/2] xdrgen - machine-generated XDR functions Jeff Layton
@ 2024-08-21 14:06   ` Chuck Lever III
  0 siblings, 0 replies; 16+ messages in thread
From: Chuck Lever III @ 2024-08-21 14:06 UTC (permalink / raw)
  To: Jeff Layton; +Cc: Chuck Lever, Linux NFS Mailing List



> On Aug 21, 2024, at 10:03 AM, Jeff Layton <jlayton@kernel.org> wrote:
> 
> On Tue, 2024-08-20 at 10:45 -0400, cel@kernel.org wrote:
>> From: Chuck Lever <chuck.lever@oracle.com>
>> 
>> For a description of this work-in-progress, see the new README file.
>> For unfinished work items, ditto.
>> 
>> The first patch adds the tool.
>> 
>> The second patch is an nfs4_1.x file that I created to confirm that
>> the tool indeed works for Jeff's purposes. Actual generated header
>> and source are in fs/nfsd/nfs4xdr_gen.? .
>> 
>> Feel free to ignore 2/2. It is included here only to demonstrate the
>> tool's new "pragma" directives that I think can avoid the need to
>> hand-edit generated source, as discussed in an earlier thread. Grep
>> the new README or nfs4_1.x for "pragma".
>> 
>> These patches apply to v6.11-rc4, but can be rebased on nfsd-next if
>> they are found merge-worthy. See also:
>> 
>> https://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git/log/?h=lkxdrgen
>> 
>> For review, note that the numerous .j2 files are all tiny and can be
>> skimmed in favor of the .py files, where the tool's logic is.
>> 
>> All Python-related advice and guidance is gratefully accepted.
>> 
>> --
>> 
>> Chuck Lever (2):
>>  tools: Add xdrgen
>>  NFSD: Create an initial nfs4_1.x file
>> 
>> fs/nfsd/nfs4_1.x                              | 164 +++++++
>> fs/nfsd/nfs4xdr_gen.c                         | 236 ++++++++++
>> fs/nfsd/nfs4xdr_gen.h                         | 113 +++++
>> include/linux/sunrpc/xdrgen-builtins.h        | 253 ++++++++++
>> tools/net/sunrpc/xdrgen/.gitignore            |   2 +
>> tools/net/sunrpc/xdrgen/README                | 231 +++++++++
>> tools/net/sunrpc/xdrgen/__init__.py           |   0
>> tools/net/sunrpc/xdrgen/common.py             |  21 +
>> .../net/sunrpc/xdrgen/generators/__init__.py  |   0
>> .../sunrpc/xdrgen/generators/boilerplate.py   |  66 +++
>> .../net/sunrpc/xdrgen/generators/constant.py  |  38 ++
>> tools/net/sunrpc/xdrgen/generators/enum.py    |  67 +++
>> tools/net/sunrpc/xdrgen/generators/program.py |  96 ++++
>> tools/net/sunrpc/xdrgen/generators/struct.py  | 435 +++++++++++++++++
>> tools/net/sunrpc/xdrgen/generators/typedef.py | 282 +++++++++++
>> tools/net/sunrpc/xdrgen/generators/union.py   | 297 ++++++++++++
>> tools/net/sunrpc/xdrgen/subcmds/__init__.py   |   0
>> tools/net/sunrpc/xdrgen/subcmds/header.py     |  62 +++
>> tools/net/sunrpc/xdrgen/subcmds/lint.py       |  28 ++
>> tools/net/sunrpc/xdrgen/subcmds/source.py     |  82 ++++
>> .../templates/C/boilerplate/header_bottom.j2  |   3 +
>> .../templates/C/boilerplate/header_top.j2     |  11 +
>> .../templates/C/boilerplate/source_top.j2     |   5 +
>> .../templates/C/constants/declaration.j2      |   5 +
>> .../templates/C/enum/declaration/close.j2     |   7 +
>> .../C/enum/declaration/enumerator.j2          |   2 +
>> .../templates/C/enum/declaration/open.j2      |   3 +
>> .../xdrgen/templates/C/enum/decoder/enum.j2   |  19 +
>> .../xdrgen/templates/C/enum/encoder/enum.j2   |  14 +
>> .../C/program/declaration/argument.j2         |   2 +
>> .../templates/C/program/declaration/result.j2 |   2 +
>> .../templates/C/program/decoder/argument.j2   |  21 +
>> .../templates/C/program/encoder/result.j2     |  21 +
>> .../templates/C/struct/declaration/basic.j2   |   5 +
>> .../templates/C/struct/declaration/close.j2   |   7 +
>> .../struct/declaration/fixed_length_array.j2  |   5 +
>> .../struct/declaration/fixed_length_opaque.j2 |   5 +
>> .../templates/C/struct/declaration/open.j2    |   6 +
>> .../C/struct/declaration/optional_data.j2     |   5 +
>> .../C/struct/declaration/pointer-open.j2      |   6 +
>> .../declaration/variable_length_array.j2      |   8 +
>> .../declaration/variable_length_opaque.j2     |   5 +
>> .../declaration/variable_length_string.j2     |   5 +
>> .../templates/C/struct/decoder/basic.j2       |   6 +
>> .../templates/C/struct/decoder/close.j2       |   3 +
>> .../C/struct/decoder/fixed_length_array.j2    |   8 +
>> .../C/struct/decoder/fixed_length_opaque.j2   |   6 +
>> .../xdrgen/templates/C/struct/decoder/open.j2 |  12 +
>> .../C/struct/decoder/optional_data.j2         |   6 +
>> .../C/struct/decoder/pointer-open.j2          |  22 +
>> .../C/struct/decoder/variable_length_array.j2 |  13 +
>> .../struct/decoder/variable_length_opaque.j2  |   6 +
>> .../struct/decoder/variable_length_string.j2  |   6 +
>> .../templates/C/struct/encoder/basic.j2       |  10 +
>> .../templates/C/struct/encoder/close.j2       |   3 +
>> .../C/struct/encoder/fixed_length_array.j2    |  12 +
>> .../C/struct/encoder/fixed_length_opaque.j2   |   6 +
>> .../xdrgen/templates/C/struct/encoder/open.j2 |  12 +
>> .../C/struct/encoder/optional_data.j2         |   6 +
>> .../C/struct/encoder/pointer-open.j2          |  20 +
>> .../C/struct/encoder/variable_length_array.j2 |  15 +
>> .../struct/encoder/variable_length_opaque.j2  |   8 +
>> .../struct/encoder/variable_length_string.j2  |   8 +
>> .../templates/C/typedef/declaration/basic.j2  |  11 +
>> .../typedef/declaration/fixed_length_array.j2 |  11 +
>> .../declaration/fixed_length_opaque.j2        |  11 +
>> .../declaration/variable_length_array.j2      |  14 +
>> .../declaration/variable_length_opaque.j2     |  11 +
>> .../declaration/variable_length_string.j2     |  11 +
>> .../templates/C/typedef/decoder/basic.j2      |  17 +
>> .../C/typedef/decoder/fixed_length_array.j2   |  25 +
>> .../C/typedef/decoder/fixed_length_opaque.j2  |  17 +
>> .../typedef/decoder/variable_length_array.j2  |  26 ++
>> .../typedef/decoder/variable_length_opaque.j2 |  17 +
>> .../typedef/decoder/variable_length_string.j2 |  17 +
>> .../templates/C/typedef/encoder/basic.j2      |  21 +
>> .../C/typedef/encoder/fixed_length_array.j2   |  25 +
>> .../C/typedef/encoder/fixed_length_opaque.j2  |  17 +
>> .../typedef/encoder/variable_length_array.j2  |  30 ++
>> .../typedef/encoder/variable_length_opaque.j2 |  17 +
>> .../typedef/encoder/variable_length_string.j2 |  17 +
>> .../C/union/declaration/case_spec.j2          |   2 +
>> .../templates/C/union/declaration/close.j2    |   8 +
>> .../C/union/declaration/default_spec.j2       |   2 +
>> .../templates/C/union/declaration/open.j2     |   6 +
>> .../C/union/declaration/switch_spec.j2        |   3 +
>> .../xdrgen/templates/C/union/decoder/basic.j2 |   6 +
>> .../xdrgen/templates/C/union/decoder/break.j2 |   2 +
>> .../templates/C/union/decoder/case_spec.j2    |   2 +
>> .../xdrgen/templates/C/union/decoder/close.j2 |   4 +
>> .../templates/C/union/decoder/default_spec.j2 |   2 +
>> .../xdrgen/templates/C/union/decoder/open.j2  |  12 +
>> .../C/union/decoder/optional_data.j2          |   6 +
>> .../templates/C/union/decoder/switch_spec.j2  |   7 +
>> .../C/union/decoder/variable_length_array.j2  |  13 +
>> .../C/union/decoder/variable_length_opaque.j2 |   6 +
>> .../C/union/decoder/variable_length_string.j2 |   6 +
>> .../xdrgen/templates/C/union/decoder/void.j2  |   3 +
>> .../xdrgen/templates/C/union/encoder/basic.j2 |  10 +
>> .../xdrgen/templates/C/union/encoder/break.j2 |   2 +
>> .../templates/C/union/encoder/case_spec.j2    |   2 +
>> .../xdrgen/templates/C/union/encoder/close.j2 |   4 +
>> .../templates/C/union/encoder/default_spec.j2 |   2 +
>> .../xdrgen/templates/C/union/encoder/open.j2  |  12 +
>> .../templates/C/union/encoder/switch_spec.j2  |   7 +
>> .../xdrgen/templates/C/union/encoder/void.j2  |   3 +
>> tools/net/sunrpc/xdrgen/tests/test.x          |  36 ++
>> tools/net/sunrpc/xdrgen/xdr                   |   1 +
>> tools/net/sunrpc/xdrgen/xdr.ebnf              | 117 +++++
>> tools/net/sunrpc/xdrgen/xdr_ast.py            | 437 ++++++++++++++++++
>> tools/net/sunrpc/xdrgen/xdr_parse.py          |  20 +
>> tools/net/sunrpc/xdrgen/xdrgen                |  95 ++++
>> 112 files changed, 3986 insertions(+)
>> create mode 100644 fs/nfsd/nfs4_1.x
>> create mode 100644 fs/nfsd/nfs4xdr_gen.c
>> create mode 100644 fs/nfsd/nfs4xdr_gen.h
>> create mode 100644 include/linux/sunrpc/xdrgen-builtins.h
>> create mode 100644 tools/net/sunrpc/xdrgen/.gitignore
>> create mode 100644 tools/net/sunrpc/xdrgen/README
>> create mode 100644 tools/net/sunrpc/xdrgen/__init__.py
>> create mode 100644 tools/net/sunrpc/xdrgen/common.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/__init__.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/boilerplate.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/constant.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/enum.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/program.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/struct.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/typedef.py
>> create mode 100644 tools/net/sunrpc/xdrgen/generators/union.py
>> create mode 100644 tools/net/sunrpc/xdrgen/subcmds/__init__.py
>> create mode 100755 tools/net/sunrpc/xdrgen/subcmds/header.py
>> create mode 100755 tools/net/sunrpc/xdrgen/subcmds/lint.py
>> create mode 100755 tools/net/sunrpc/xdrgen/subcmds/source.py
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_bottom.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/header_top.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/boilerplate/source_top.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/constants/declaration.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enumerator.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/fixed_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/optional_data.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/pointer-open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/variable_length_string.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/pointer-open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/pointer-open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/case_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/default_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/switch_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2
>> create mode 100644 tools/net/sunrpc/xdrgen/tests/test.x
>> create mode 120000 tools/net/sunrpc/xdrgen/xdr
>> create mode 100644 tools/net/sunrpc/xdrgen/xdr.ebnf
>> create mode 100644 tools/net/sunrpc/xdrgen/xdr_ast.py
>> create mode 100644 tools/net/sunrpc/xdrgen/xdr_parse.py
>> create mode 100755 tools/net/sunrpc/xdrgen/xdrgen
>> 
> 
> This all looks great to me. I really support autogenerating our XDR
> boilerplate, particularly as I've been looking at implementing
> CB_NOTIFY (which would be quite tedious to roll by hand). This would
> make it a lot simpler and less error prone.
> 
> Do you have these patches in a branch in your tree that I could pick
> from?

Mentioned above:

>> These patches apply to v6.11-rc4, but can be rebased on nfsd-next if
>> they are found merge-worthy. See also:
>> 
>> https://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git/log/?h=lkxdrgen


The lkxdrgen branch in my kernel.org <http://kernel.org/> git repo.

However, do you need it rebased on nfsd-next? I can do that today if so.


--
Chuck Lever



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-20 14:46 ` [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file cel
@ 2024-08-21 14:22   ` Jeff Layton
  2024-08-21 14:38     ` Chuck Lever
                       ` (2 more replies)
  2024-08-22 16:34   ` Jeff Layton
  1 sibling, 3 replies; 16+ messages in thread
From: Jeff Layton @ 2024-08-21 14:22 UTC (permalink / raw)
  To: cel, linux-nfs; +Cc: Chuck Lever

On Tue, 2024-08-20 at 10:46 -0400, cel@kernel.org wrote:
> From: Chuck Lever <chuck.lever@oracle.com>
> 
> Build an NFSv4 protocol snippet to support the delstid extensions.
> The new fs/nfsd/nfs4_1.x file can be added to over time as other
> parts of NFSD's XDR functions are converted to machine-generated
> code.
> 
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>  fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
>  fs/nfsd/nfs4xdr_gen.c | 236 ++++++++++++++++++++++++++++++++++++++++++
>  fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
>  3 files changed, 513 insertions(+)
>  create mode 100644 fs/nfsd/nfs4_1.x
>  create mode 100644 fs/nfsd/nfs4xdr_gen.c
>  create mode 100644 fs/nfsd/nfs4xdr_gen.h
> 

I see the patches in your lkxdrgen branch. I gave this a try and
started rebasing my delstid work on top of it, but I hit the same
symbol conflicts I hit before once I started trying to include the
full-blown nfs4xdr_gen.h header:

------------------------8<---------------------------
In file included from fs/nfsd/nfs4xdr.c:58:
fs/nfsd/nfs4xdr_gen.h:86:9: error: redeclaration of enumerator ‘FATTR4_OPEN_ARGUMENTS’
   86 |         FATTR4_OPEN_ARGUMENTS = 86
      |         ^~~~~~~~~~~~~~~~~~~~~
In file included from fs/nfsd/nfsfh.h:15,
                 from fs/nfsd/state.h:41,
                 from fs/nfsd/xdr4.h:40,
                 from fs/nfsd/nfs4xdr.c:51:
./include/linux/nfs4.h:518:9: note: previous definition of ‘FATTR4_OPEN_ARGUMENTS’ with type ‘enum <anonymous>’
  518 |         FATTR4_OPEN_ARGUMENTS           = 86,
      |         ^~~~~~~~~~~~~~~~~~~~~
fs/nfsd/nfs4xdr_gen.h:102:9: error: redeclaration of enumerator ‘FATTR4_TIME_DELEG_ACCESS’
  102 |         FATTR4_TIME_DELEG_ACCESS = 84
      |         ^~~~~~~~~~~~~~~~~~~~~~~~
./include/linux/nfs4.h:516:9: note: previous definition of ‘FATTR4_TIME_DELEG_ACCESS’ with type ‘enum <anonymous>’
  516 |         FATTR4_TIME_DELEG_ACCESS        = 84,
      |         ^~~~~~~~~~~~~~~~~~~~~~~~
fs/nfsd/nfs4xdr_gen.h:106:9: error: redeclaration of enumerator ‘FATTR4_TIME_DELEG_MODIFY’
  106 |         FATTR4_TIME_DELEG_MODIFY = 85
      |         ^~~~~~~~~~~~~~~~~~~~~~~~
./include/linux/nfs4.h:517:9: note: previous definition of ‘FATTR4_TIME_DELEG_MODIFY’ with type ‘enum <anonymous>’
  517 |         FATTR4_TIME_DELEG_MODIFY        = 85,
      |         ^~~~~~~~~~~~~~~~~~~~~~~~
------------------------8<---------------------------

I'm not sure of the best way to work around this, unless we want to try
to split up nfs4.h.

Also, as a side note:

fs/nfsd/nfs4xdr.c: In function ‘nfsd4_encode_fattr4_open_arguments’:
fs/nfsd/nfs4xdr.c:3446:55: error: incompatible type for argument 2 of ‘xdrgen_encode_fattr4_open_arguments’
 3446 |         if (!xdrgen_encode_fattr4_open_arguments(xdr, &nfsd_open_arguments))


OPEN_ARGUMENTS4 is a large structure with 5 different bitmaps in it. We
probably don't want to pass that by value. When the tool is dealing
with a struct, we should have it generate functions that take a pointer
instead (IMO).

> diff --git a/fs/nfsd/nfs4_1.x b/fs/nfsd/nfs4_1.x
> new file mode 100644
> index 000000000000..d2fde450de5e
> --- /dev/null
> +++ b/fs/nfsd/nfs4_1.x
> @@ -0,0 +1,164 @@
> +/*
> + * Copyright (c) 2010 IETF Trust and the persons identified
> + * as the document authors.  All rights reserved.
> + *
> + * The document authors are identified in RFC 3530 and
> + * RFC 5661.
> + *
> + * Redistribution and use in source and binary forms, with
> + * or without modification, are permitted provided that the
> + * following conditions are met:
> + *
> + * - Redistributions of source code must retain the above
> + *   copyright notice, this list of conditions and the
> + *   following disclaimer.
> + *
> + * - Redistributions in binary form must reproduce the above
> + *   copyright notice, this list of conditions and the
> + *   following disclaimer in the documentation and/or other
> + *   materials provided with the distribution.
> + *
> + * - Neither the name of Internet Society, IETF or IETF
> + *   Trust, nor the names of specific contributors, may be
> + *   used to endorse or promote products derived from this
> + *   software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
> + *   AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
> + *   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> + *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
> + *   EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
> + *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> + *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + *   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> + *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> + *   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
> + *   IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> + *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +pragma header nfs4;
> +
> +/*
> + * Basic typedefs for RFC 1832 data type definitions
> + */
> +typedef hyper		int64_t;
> +typedef unsigned int	uint32_t;
> +
> +/*
> + * Basic data types
> + */
> +typedef uint32_t	bitmap4<>;
> +
> +/*
> + * Timeval
> + */
> +struct nfstime4 {
> +	int64_t		seconds;
> +	uint32_t	nseconds;
> +};
> +
> +
> +/*
> + * The following content was extracted from draft-ietf-nfsv4-delstid
> + */
> +
> +typedef bool            fattr4_offline;
> +
> +
> +const FATTR4_OFFLINE            = 83;
> +
> +
> +struct open_arguments4 {
> +  bitmap4  oa_share_access;
> +  bitmap4  oa_share_deny;
> +  bitmap4  oa_share_access_want;
> +  bitmap4  oa_open_claim;
> +  bitmap4  oa_create_mode;
> +};
> +
> +
> +enum open_args_share_access4 {
> +   OPEN_ARGS_SHARE_ACCESS_READ  = 1,
> +   OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
> +   OPEN_ARGS_SHARE_ACCESS_BOTH  = 3
> +};
> +
> +
> +enum open_args_share_deny4 {
> +   OPEN_ARGS_SHARE_DENY_NONE  = 0,
> +   OPEN_ARGS_SHARE_DENY_READ  = 1,
> +   OPEN_ARGS_SHARE_DENY_WRITE = 2,
> +   OPEN_ARGS_SHARE_DENY_BOTH  = 3
> +};
> +
> +
> +enum open_args_share_access_want4 {
> +   OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG           = 3,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG            = 4,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL              = 5,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL
> +                                                   = 17,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED
> +                                                   = 18,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS    = 20,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21
> +};
> +
> +
> +enum open_args_open_claim4 {
> +   OPEN_ARGS_OPEN_CLAIM_NULL          = 0,
> +   OPEN_ARGS_OPEN_CLAIM_PREVIOUS      = 1,
> +   OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR  = 2,
> +   OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
> +   OPEN_ARGS_OPEN_CLAIM_FH            = 4,
> +   OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH  = 5,
> +   OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6
> +};
> +
> +
> +enum open_args_createmode4 {
> +   OPEN_ARGS_CREATEMODE_UNCHECKED4     = 0,
> +   OPEN_ARGS_CREATE_MODE_GUARDED       = 1,
> +   OPEN_ARGS_CREATEMODE_EXCLUSIVE4     = 2,
> +   OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1  = 3
> +};
> +
> +
> +typedef open_arguments4 fattr4_open_arguments;
> +pragma public fattr4_open_arguments;
> +
> +
> +%/*
> +% * Determine what OPEN supports.
> +% */
> +const FATTR4_OPEN_ARGUMENTS     = 86;
> +
> +
> +const OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000;
> +
> +
> +const OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010;
> +
> +
> +/*
> + * attributes for the delegation times being
> + * cached and served by the "client"
> + */
> +typedef nfstime4        fattr4_time_deleg_access;
> +typedef nfstime4        fattr4_time_deleg_modify;
> +
> +
> +%/*
> +% * New RECOMMENDED Attribute for
> +% * delegation caching of times
> +% */
> +const FATTR4_TIME_DELEG_ACCESS  = 84;
> +const FATTR4_TIME_DELEG_MODIFY  = 85;
> +
> +
> +const OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000;
> +
> diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
> new file mode 100644
> index 000000000000..bb714859d6c6
> --- /dev/null
> +++ b/fs/nfsd/nfs4xdr_gen.c
> @@ -0,0 +1,236 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Generated by xdrgen. Manual edits will be lost.
> +// XDR specification modification time: Mon Aug 19 23:28:23 2024
> +
> +#include "nfs4xdr_gen.h"
> +
> +static bool __maybe_unused
> +xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
> +{
> +	return xdrgen_decode_hyper(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
> +{
> +	return xdrgen_decode_unsigned_int(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
> +{
> +	if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
> +		return false;
> +	for (u32 i = 0; i < ptr->count; i++)
> +		if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i]))
> +			return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
> +{
> +	if (!xdrgen_decode_int64_t(xdr, &ptr->seconds))
> +		return false;
> +	if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
> +{
> +	return xdrgen_decode_bool(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr)
> +{
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_deny))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access_want))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_open_claim))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +bool
> +xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr)
> +{
> +	return xdrgen_decode_open_arguments4(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
> +{
> +	return xdrgen_decode_nfstime4(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr)
> +{
> +	return xdrgen_decode_nfstime4(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
> +{
> +	return xdrgen_encode_hyper(xdr, value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
> +{
> +	return xdrgen_encode_unsigned_int(xdr, value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
> +{
> +	if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
> +		return false;
> +	for (u32 i = 0; i < value.count; i++)
> +		if (!xdrgen_encode_uint32_t(xdr, value.element[i]))
> +			return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
> +{
> +	if (!xdrgen_encode_int64_t(xdr, value->seconds))
> +		return false;
> +	if (!xdrgen_encode_uint32_t(xdr, value->nseconds))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
> +{
> +	return xdrgen_encode_bool(xdr, value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value)
> +{
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_deny))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access_want))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_open_claim))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +bool
> +xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value)
> +{
> +	return xdrgen_encode_open_arguments4(xdr, &value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access value)
> +{
> +	return xdrgen_encode_nfstime4(xdr, &value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify value)
> +{
> +	return xdrgen_encode_nfstime4(xdr, &value);
> +};
> diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
> new file mode 100644
> index 000000000000..27c601b36580
> --- /dev/null
> +++ b/fs/nfsd/nfs4xdr_gen.h
> @@ -0,0 +1,113 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Generated by xdrgen. Manual edits will be lost. */
> +/* XDR specification modification time: Mon Aug 19 23:28:23 2024 */
> +
> +#ifndef _LINUX_NFS4_XDRGEN_H
> +#define _LINUX_NFS4_XDRGEN_H
> +
> +#include <linux/types.h>
> +#include <linux/sunrpc/svc.h>
> +
> +#include <linux/sunrpc/xdrgen-builtins.h>
> +
> +typedef s64 int64_t;
> +
> +typedef u32 uint32_t;
> +
> +typedef struct {
> +	u32 count;
> +	uint32_t *element;
> +} bitmap4;
> +
> +struct nfstime4 {
> +	int64_t seconds;
> +	uint32_t nseconds;
> +};
> +
> +typedef bool fattr4_offline;
> +
> +enum {
> +	FATTR4_OFFLINE = 83
> +};
> +
> +struct open_arguments4 {
> +	bitmap4 oa_share_access;
> +	bitmap4 oa_share_deny;
> +	bitmap4 oa_share_access_want;
> +	bitmap4 oa_open_claim;
> +	bitmap4 oa_create_mode;
> +};
> +
> +enum open_args_share_access4 {
> +	OPEN_ARGS_SHARE_ACCESS_READ = 1,
> +	OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
> +	OPEN_ARGS_SHARE_ACCESS_BOTH = 3,
> +};
> +
> +enum open_args_share_deny4 {
> +	OPEN_ARGS_SHARE_DENY_NONE = 0,
> +	OPEN_ARGS_SHARE_DENY_READ = 1,
> +	OPEN_ARGS_SHARE_DENY_WRITE = 2,
> +	OPEN_ARGS_SHARE_DENY_BOTH = 3,
> +};
> +
> +enum open_args_share_access_want4 {
> +	OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG = 3,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG = 4,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL = 5,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 17,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 18,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21,
> +};
> +
> +enum open_args_open_claim4 {
> +	OPEN_ARGS_OPEN_CLAIM_NULL = 0,
> +	OPEN_ARGS_OPEN_CLAIM_PREVIOUS = 1,
> +	OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR = 2,
> +	OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
> +	OPEN_ARGS_OPEN_CLAIM_FH = 4,
> +	OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5,
> +	OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6,
> +};
> +
> +enum open_args_createmode4 {
> +	OPEN_ARGS_CREATEMODE_UNCHECKED4 = 0,
> +	OPEN_ARGS_CREATE_MODE_GUARDED = 1,
> +	OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2,
> +	OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3,
> +};
> +
> +typedef struct open_arguments4 fattr4_open_arguments;
> +bool xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr);
> +bool xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value);
> +
> +enum {
> +	FATTR4_OPEN_ARGUMENTS = 86
> +};
> +
> +enum {
> +	OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000
> +};
> +
> +enum {
> +	OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010
> +};
> +
> +typedef struct nfstime4 fattr4_time_deleg_access;
> +
> +typedef struct nfstime4 fattr4_time_deleg_modify;
> +
> +enum {
> +	FATTR4_TIME_DELEG_ACCESS = 84
> +};
> +
> +enum {
> +	FATTR4_TIME_DELEG_MODIFY = 85
> +};
> +
> +enum {
> +	OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000
> +};
> +
> +#endif /* _LINUX_NFS4_XDRGEN_H */

-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 14:22   ` Jeff Layton
@ 2024-08-21 14:38     ` Chuck Lever
  2024-08-21 16:51       ` Jeff Layton
  2024-08-21 15:00     ` Christoph Hellwig
  2024-08-21 19:03     ` Chuck Lever
  2 siblings, 1 reply; 16+ messages in thread
From: Chuck Lever @ 2024-08-21 14:38 UTC (permalink / raw)
  To: Jeff Layton; +Cc: cel, linux-nfs

On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> On Tue, 2024-08-20 at 10:46 -0400, cel@kernel.org wrote:
> > From: Chuck Lever <chuck.lever@oracle.com>
> > 
> > Build an NFSv4 protocol snippet to support the delstid extensions.
> > The new fs/nfsd/nfs4_1.x file can be added to over time as other
> > parts of NFSD's XDR functions are converted to machine-generated
> > code.
> > 
> > Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > ---
> >  fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
> >  fs/nfsd/nfs4xdr_gen.c | 236 ++++++++++++++++++++++++++++++++++++++++++
> >  fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
> >  3 files changed, 513 insertions(+)
> >  create mode 100644 fs/nfsd/nfs4_1.x
> >  create mode 100644 fs/nfsd/nfs4xdr_gen.c
> >  create mode 100644 fs/nfsd/nfs4xdr_gen.h
> > 
> 
> I see the patches in your lkxdrgen branch. I gave this a try and
> started rebasing my delstid work on top of it, but I hit the same
> symbol conflicts I hit before once I started trying to include the
> full-blown nfs4xdr_gen.h header:
> 
> ------------------------8<---------------------------
> In file included from fs/nfsd/nfs4xdr.c:58:
> fs/nfsd/nfs4xdr_gen.h:86:9: error: redeclaration of enumerator ‘FATTR4_OPEN_ARGUMENTS’
>    86 |         FATTR4_OPEN_ARGUMENTS = 86
>       |         ^~~~~~~~~~~~~~~~~~~~~
> In file included from fs/nfsd/nfsfh.h:15,
>                  from fs/nfsd/state.h:41,
>                  from fs/nfsd/xdr4.h:40,
>                  from fs/nfsd/nfs4xdr.c:51:
> ./include/linux/nfs4.h:518:9: note: previous definition of ‘FATTR4_OPEN_ARGUMENTS’ with type ‘enum <anonymous>’
>   518 |         FATTR4_OPEN_ARGUMENTS           = 86,
>       |         ^~~~~~~~~~~~~~~~~~~~~
> fs/nfsd/nfs4xdr_gen.h:102:9: error: redeclaration of enumerator ‘FATTR4_TIME_DELEG_ACCESS’
>   102 |         FATTR4_TIME_DELEG_ACCESS = 84
>       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> ./include/linux/nfs4.h:516:9: note: previous definition of ‘FATTR4_TIME_DELEG_ACCESS’ with type ‘enum <anonymous>’
>   516 |         FATTR4_TIME_DELEG_ACCESS        = 84,
>       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> fs/nfsd/nfs4xdr_gen.h:106:9: error: redeclaration of enumerator ‘FATTR4_TIME_DELEG_MODIFY’
>   106 |         FATTR4_TIME_DELEG_MODIFY = 85
>       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> ./include/linux/nfs4.h:517:9: note: previous definition of ‘FATTR4_TIME_DELEG_MODIFY’ with type ‘enum <anonymous>’
>   517 |         FATTR4_TIME_DELEG_MODIFY        = 85,
>       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> ------------------------8<---------------------------
> 
> I'm not sure of the best way to work around this, unless we want to try
> to split up nfs4.h.

That header is shared with the client, so I consider it immutable
for our purposes here.

One option would be to namespace the generated data items. Eg, name
them:

	XG_FATTR4_TIME_DELEG_ACCESS
	XG_FATTR4_TIME_DELEG_MODIFY

That way they don't conflict with existing definitions.


> Also, as a side note:
> 
> fs/nfsd/nfs4xdr.c: In function ‘nfsd4_encode_fattr4_open_arguments’:
> fs/nfsd/nfs4xdr.c:3446:55: error: incompatible type for argument 2 of ‘xdrgen_encode_fattr4_open_arguments’
>  3446 |         if (!xdrgen_encode_fattr4_open_arguments(xdr, &nfsd_open_arguments))
> 
> 
> OPEN_ARGUMENTS4 is a large structure with 5 different bitmaps in it. We
> probably don't want to pass that by value. When the tool is dealing
> with a struct, we should have it generate functions that take a pointer
> instead (IMO).

The decoders are passed structs by reference already, fwiw. I had
been considering the same for the encoder functions, for efficiency.
I can give it a try.


-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 14:22   ` Jeff Layton
  2024-08-21 14:38     ` Chuck Lever
@ 2024-08-21 15:00     ` Christoph Hellwig
  2024-08-21 15:59       ` Jeff Layton
  2024-08-21 19:03     ` Chuck Lever
  2 siblings, 1 reply; 16+ messages in thread
From: Christoph Hellwig @ 2024-08-21 15:00 UTC (permalink / raw)
  To: Jeff Layton; +Cc: cel, linux-nfs, Chuck Lever

On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> I'm not sure of the best way to work around this, unless we want to try
> to split up nfs4.h.

Can we just replace the structure definitions with the generated ones
ASAP in one big patch?  And then only do the marshalling code piece by
piece as needed/wanted?

> 
> Also, as a side note:
> 
> fs/nfsd/nfs4xdr.c: In function ‘nfsd4_encode_fattr4_open_arguments’:
> fs/nfsd/nfs4xdr.c:3446:55: error: incompatible type for argument 2 of ‘xdrgen_encode_fattr4_open_arguments’
>  3446 |         if (!xdrgen_encode_fattr4_open_arguments(xdr, &nfsd_open_arguments))
> 
> 
> OPEN_ARGUMENTS4 is a large structure with 5 different bitmaps in it. We
> probably don't want to pass that by value. When the tool is dealing
> with a struct, we should have it generate functions that take a pointer
> instead (IMO).

Yes.  Probably marked const to clarify that it isn't supposed to be
modified.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 15:00     ` Christoph Hellwig
@ 2024-08-21 15:59       ` Jeff Layton
  0 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2024-08-21 15:59 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: cel, linux-nfs, Chuck Lever

On Wed, 2024-08-21 at 08:00 -0700, Christoph Hellwig wrote:
> On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> > I'm not sure of the best way to work around this, unless we want to
> > try
> > to split up nfs4.h.
> 
> Can we just replace the structure definitions with the generated ones
> ASAP in one big patch?  And then only do the marshalling code piece
> by
> piece as needed/wanted?
> 

I'll have a look. That probably means that we'll need to split nfs4.h,
as not everything in there is defined by the protocol headers.

> > 
> > Also, as a side note:
> > 
> > fs/nfsd/nfs4xdr.c: In function
> > ‘nfsd4_encode_fattr4_open_arguments’:
> > fs/nfsd/nfs4xdr.c:3446:55: error: incompatible type for argument 2
> > of ‘xdrgen_encode_fattr4_open_arguments’
> >  3446 |         if (!xdrgen_encode_fattr4_open_arguments(xdr,
> > &nfsd_open_arguments))
> > 
> > 
> > OPEN_ARGUMENTS4 is a large structure with 5 different bitmaps in
> > it. We
> > probably don't want to pass that by value. When the tool is dealing
> > with a struct, we should have it generate functions that take a
> > pointer
> > instead (IMO).
> 
> Yes.  Probably marked const to clarify that it isn't supposed to be
> modified.
> 
> 

Definitely, on the encoding side. Obviously, we can't on the decoding
side.
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 14:38     ` Chuck Lever
@ 2024-08-21 16:51       ` Jeff Layton
  2024-08-21 17:03         ` Chuck Lever
  0 siblings, 1 reply; 16+ messages in thread
From: Jeff Layton @ 2024-08-21 16:51 UTC (permalink / raw)
  To: Chuck Lever; +Cc: cel, linux-nfs, Trond Myklebust, Anna Schumaker

On Wed, 2024-08-21 at 10:38 -0400, Chuck Lever wrote:
> On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> > On Tue, 2024-08-20 at 10:46 -0400, cel@kernel.org wrote:
> > > From: Chuck Lever <chuck.lever@oracle.com>
> > > 
> > > Build an NFSv4 protocol snippet to support the delstid
> > > extensions.
> > > The new fs/nfsd/nfs4_1.x file can be added to over time as other
> > > parts of NFSD's XDR functions are converted to machine-generated
> > > code.
> > > 
> > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > > ---
> > >  fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
> > >  fs/nfsd/nfs4xdr_gen.c | 236
> > > ++++++++++++++++++++++++++++++++++++++++++
> > >  fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
> > >  3 files changed, 513 insertions(+)
> > >  create mode 100644 fs/nfsd/nfs4_1.x
> > >  create mode 100644 fs/nfsd/nfs4xdr_gen.c
> > >  create mode 100644 fs/nfsd/nfs4xdr_gen.h
> > > 
> > 
> > I see the patches in your lkxdrgen branch. I gave this a try and
> > started rebasing my delstid work on top of it, but I hit the same
> > symbol conflicts I hit before once I started trying to include the
> > full-blown nfs4xdr_gen.h header:
> > 
> > ------------------------8<---------------------------
> > In file included from fs/nfsd/nfs4xdr.c:58:
> > fs/nfsd/nfs4xdr_gen.h:86:9: error: redeclaration of enumerator
> > ‘FATTR4_OPEN_ARGUMENTS’
> >    86 |         FATTR4_OPEN_ARGUMENTS = 86
> >       |         ^~~~~~~~~~~~~~~~~~~~~
> > In file included from fs/nfsd/nfsfh.h:15,
> >                  from fs/nfsd/state.h:41,
> >                  from fs/nfsd/xdr4.h:40,
> >                  from fs/nfsd/nfs4xdr.c:51:
> > ./include/linux/nfs4.h:518:9: note: previous definition of
> > ‘FATTR4_OPEN_ARGUMENTS’ with type ‘enum <anonymous>’
> >   518 |         FATTR4_OPEN_ARGUMENTS           = 86,
> >       |         ^~~~~~~~~~~~~~~~~~~~~
> > fs/nfsd/nfs4xdr_gen.h:102:9: error: redeclaration of enumerator
> > ‘FATTR4_TIME_DELEG_ACCESS’
> >   102 |         FATTR4_TIME_DELEG_ACCESS = 84
> >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > ./include/linux/nfs4.h:516:9: note: previous definition of
> > ‘FATTR4_TIME_DELEG_ACCESS’ with type ‘enum <anonymous>’
> >   516 |         FATTR4_TIME_DELEG_ACCESS        = 84,
> >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > fs/nfsd/nfs4xdr_gen.h:106:9: error: redeclaration of enumerator
> > ‘FATTR4_TIME_DELEG_MODIFY’
> >   106 |         FATTR4_TIME_DELEG_MODIFY = 85
> >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > ./include/linux/nfs4.h:517:9: note: previous definition of
> > ‘FATTR4_TIME_DELEG_MODIFY’ with type ‘enum <anonymous>’
> >   517 |         FATTR4_TIME_DELEG_MODIFY        = 85,
> >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > ------------------------8<---------------------------
> > 
> > I'm not sure of the best way to work around this, unless we want to
> > try
> > to split up nfs4.h.
> 
> That header is shared with the client, so I consider it immutable
> for our purposes here.
> 
>
> One option would be to namespace the generated data items. Eg, name
> them:
> 
> 	XG_FATTR4_TIME_DELEG_ACCESS
> 	XG_FATTR4_TIME_DELEG_MODIFY
> 
> That way they don't conflict with existing definitions.
> 

I'd rather avoid that if we can. Having things named exactly the same
as in the spec makes the code easier to read and understand.

What might be best is to autogenerate a header from a (combined)
nfs4.x, and then have the old nfs4.h file include it. Then we'd just
have to eliminate anything from nfs4.h that conflicts with the
autogenerated symbols.

That's definitely not treating include/linux/nfs4.h as immutable, but
we might still be able to avoid a lot of changes to the client code
that way.

We'd need Trond and Anna to buy in on that though.

> 
> > Also, as a side note:
> > 
> > fs/nfsd/nfs4xdr.c: In function
> > ‘nfsd4_encode_fattr4_open_arguments’:
> > fs/nfsd/nfs4xdr.c:3446:55: error: incompatible type for argument 2
> > of ‘xdrgen_encode_fattr4_open_arguments’
> >  3446 |         if (!xdrgen_encode_fattr4_open_arguments(xdr,
> > &nfsd_open_arguments))
> > 
> > 
> > OPEN_ARGUMENTS4 is a large structure with 5 different bitmaps in
> > it. We
> > probably don't want to pass that by value. When the tool is dealing
> > with a struct, we should have it generate functions that take a
> > pointer
> > instead (IMO).
> 
> The decoders are passed structs by reference already, fwiw. I had
> been considering the same for the encoder functions, for efficiency.
> I can give it a try.
> 

Sounds good.
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 16:51       ` Jeff Layton
@ 2024-08-21 17:03         ` Chuck Lever
  2024-08-21 17:34           ` Jeff Layton
  0 siblings, 1 reply; 16+ messages in thread
From: Chuck Lever @ 2024-08-21 17:03 UTC (permalink / raw)
  To: Jeff Layton; +Cc: cel, linux-nfs, Trond Myklebust, Anna Schumaker

On Wed, Aug 21, 2024 at 12:51:51PM -0400, Jeff Layton wrote:
> On Wed, 2024-08-21 at 10:38 -0400, Chuck Lever wrote:
> > On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> > > On Tue, 2024-08-20 at 10:46 -0400, cel@kernel.org wrote:
> > > > From: Chuck Lever <chuck.lever@oracle.com>
> > > > 
> > > > Build an NFSv4 protocol snippet to support the delstid
> > > > extensions.
> > > > The new fs/nfsd/nfs4_1.x file can be added to over time as other
> > > > parts of NFSD's XDR functions are converted to machine-generated
> > > > code.
> > > > 
> > > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > > > ---
> > > >  fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
> > > >  fs/nfsd/nfs4xdr_gen.c | 236
> > > > ++++++++++++++++++++++++++++++++++++++++++
> > > >  fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
> > > >  3 files changed, 513 insertions(+)
> > > >  create mode 100644 fs/nfsd/nfs4_1.x
> > > >  create mode 100644 fs/nfsd/nfs4xdr_gen.c
> > > >  create mode 100644 fs/nfsd/nfs4xdr_gen.h
> > > > 
> > > 
> > > I see the patches in your lkxdrgen branch. I gave this a try and
> > > started rebasing my delstid work on top of it, but I hit the same
> > > symbol conflicts I hit before once I started trying to include the
> > > full-blown nfs4xdr_gen.h header:
> > > 
> > > ------------------------8<---------------------------
> > > In file included from fs/nfsd/nfs4xdr.c:58:
> > > fs/nfsd/nfs4xdr_gen.h:86:9: error: redeclaration of enumerator
> > > ‘FATTR4_OPEN_ARGUMENTS’
> > >    86 |         FATTR4_OPEN_ARGUMENTS = 86
> > >       |         ^~~~~~~~~~~~~~~~~~~~~
> > > In file included from fs/nfsd/nfsfh.h:15,
> > >                  from fs/nfsd/state.h:41,
> > >                  from fs/nfsd/xdr4.h:40,
> > >                  from fs/nfsd/nfs4xdr.c:51:
> > > ./include/linux/nfs4.h:518:9: note: previous definition of
> > > ‘FATTR4_OPEN_ARGUMENTS’ with type ‘enum <anonymous>’
> > >   518 |         FATTR4_OPEN_ARGUMENTS           = 86,
> > >       |         ^~~~~~~~~~~~~~~~~~~~~
> > > fs/nfsd/nfs4xdr_gen.h:102:9: error: redeclaration of enumerator
> > > ‘FATTR4_TIME_DELEG_ACCESS’
> > >   102 |         FATTR4_TIME_DELEG_ACCESS = 84
> > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > ./include/linux/nfs4.h:516:9: note: previous definition of
> > > ‘FATTR4_TIME_DELEG_ACCESS’ with type ‘enum <anonymous>’
> > >   516 |         FATTR4_TIME_DELEG_ACCESS        = 84,
> > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > fs/nfsd/nfs4xdr_gen.h:106:9: error: redeclaration of enumerator
> > > ‘FATTR4_TIME_DELEG_MODIFY’
> > >   106 |         FATTR4_TIME_DELEG_MODIFY = 85
> > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > ./include/linux/nfs4.h:517:9: note: previous definition of
> > > ‘FATTR4_TIME_DELEG_MODIFY’ with type ‘enum <anonymous>’
> > >   517 |         FATTR4_TIME_DELEG_MODIFY        = 85,
> > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > ------------------------8<---------------------------
> > > 
> > > I'm not sure of the best way to work around this, unless we want to
> > > try
> > > to split up nfs4.h.
> > 
> > That header is shared with the client, so I consider it immutable
> > for our purposes here.
> > 
> >
> > One option would be to namespace the generated data items. Eg, name
> > them:
> > 
> > 	XG_FATTR4_TIME_DELEG_ACCESS
> > 	XG_FATTR4_TIME_DELEG_MODIFY
> > 
> > That way they don't conflict with existing definitions.
> > 
> 
> I'd rather avoid that if we can. Having things named exactly the same
> as in the spec makes the code easier to read and understand.

Agreed, matching names is one of the reasons I started this work.

My thought was that the prefixed names could be fixed eventually
after the hand-rolled code has been replaced.


> What might be best is to autogenerate a header from a (combined)
> nfs4.x, and then have the old nfs4.h file include it. Then we'd just
> have to eliminate anything from nfs4.h that conflicts with the
> autogenerated symbols.
> 
> That's definitely not treating include/linux/nfs4.h as immutable, but
> we might still be able to avoid a lot of changes to the client code
> that way.

Include the current nfs4xdr_gen.h in nfs4.h, and then remove from
nfs4.h anything that has a naming conflict?

The downside of that is that approach mixes generated and
hand-rolled code. Not an objection from me, but it was something
that made Neil uncomfortable.


> We'd need Trond and Anna to buy in on that though.

Or the client can continue to use include/linux/nfs4.h and the
server can start using its own copy of that header.

-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 17:03         ` Chuck Lever
@ 2024-08-21 17:34           ` Jeff Layton
  0 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2024-08-21 17:34 UTC (permalink / raw)
  To: Chuck Lever; +Cc: cel, linux-nfs, Trond Myklebust, Anna Schumaker

On Wed, 2024-08-21 at 13:03 -0400, Chuck Lever wrote:
> On Wed, Aug 21, 2024 at 12:51:51PM -0400, Jeff Layton wrote:
> > On Wed, 2024-08-21 at 10:38 -0400, Chuck Lever wrote:
> > > On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> > > > On Tue, 2024-08-20 at 10:46 -0400, cel@kernel.org wrote:
> > > > > From: Chuck Lever <chuck.lever@oracle.com>
> > > > > 
> > > > > Build an NFSv4 protocol snippet to support the delstid
> > > > > extensions.
> > > > > The new fs/nfsd/nfs4_1.x file can be added to over time as
> > > > > other
> > > > > parts of NFSD's XDR functions are converted to machine-
> > > > > generated
> > > > > code.
> > > > > 
> > > > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > > > > ---
> > > > >  fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
> > > > >  fs/nfsd/nfs4xdr_gen.c | 236
> > > > > ++++++++++++++++++++++++++++++++++++++++++
> > > > >  fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
> > > > >  3 files changed, 513 insertions(+)
> > > > >  create mode 100644 fs/nfsd/nfs4_1.x
> > > > >  create mode 100644 fs/nfsd/nfs4xdr_gen.c
> > > > >  create mode 100644 fs/nfsd/nfs4xdr_gen.h
> > > > > 
> > > > 
> > > > I see the patches in your lkxdrgen branch. I gave this a try
> > > > and
> > > > started rebasing my delstid work on top of it, but I hit the
> > > > same
> > > > symbol conflicts I hit before once I started trying to include
> > > > the
> > > > full-blown nfs4xdr_gen.h header:
> > > > 
> > > > ------------------------8<---------------------------
> > > > In file included from fs/nfsd/nfs4xdr.c:58:
> > > > fs/nfsd/nfs4xdr_gen.h:86:9: error: redeclaration of enumerator
> > > > ‘FATTR4_OPEN_ARGUMENTS’
> > > >    86 |         FATTR4_OPEN_ARGUMENTS = 86
> > > >       |         ^~~~~~~~~~~~~~~~~~~~~
> > > > In file included from fs/nfsd/nfsfh.h:15,
> > > >                  from fs/nfsd/state.h:41,
> > > >                  from fs/nfsd/xdr4.h:40,
> > > >                  from fs/nfsd/nfs4xdr.c:51:
> > > > ./include/linux/nfs4.h:518:9: note: previous definition of
> > > > ‘FATTR4_OPEN_ARGUMENTS’ with type ‘enum <anonymous>’
> > > >   518 |         FATTR4_OPEN_ARGUMENTS           = 86,
> > > >       |         ^~~~~~~~~~~~~~~~~~~~~
> > > > fs/nfsd/nfs4xdr_gen.h:102:9: error: redeclaration of enumerator
> > > > ‘FATTR4_TIME_DELEG_ACCESS’
> > > >   102 |         FATTR4_TIME_DELEG_ACCESS = 84
> > > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > > ./include/linux/nfs4.h:516:9: note: previous definition of
> > > > ‘FATTR4_TIME_DELEG_ACCESS’ with type ‘enum <anonymous>’
> > > >   516 |         FATTR4_TIME_DELEG_ACCESS        = 84,
> > > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > > fs/nfsd/nfs4xdr_gen.h:106:9: error: redeclaration of enumerator
> > > > ‘FATTR4_TIME_DELEG_MODIFY’
> > > >   106 |         FATTR4_TIME_DELEG_MODIFY = 85
> > > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > > ./include/linux/nfs4.h:517:9: note: previous definition of
> > > > ‘FATTR4_TIME_DELEG_MODIFY’ with type ‘enum <anonymous>’
> > > >   517 |         FATTR4_TIME_DELEG_MODIFY        = 85,
> > > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~
> > > > ------------------------8<---------------------------
> > > > 
> > > > I'm not sure of the best way to work around this, unless we
> > > > want to
> > > > try
> > > > to split up nfs4.h.
> > > 
> > > That header is shared with the client, so I consider it immutable
> > > for our purposes here.
> > > 
> > > 
> > > One option would be to namespace the generated data items. Eg,
> > > name
> > > them:
> > > 
> > > 	XG_FATTR4_TIME_DELEG_ACCESS
> > > 	XG_FATTR4_TIME_DELEG_MODIFY
> > > 
> > > That way they don't conflict with existing definitions.
> > > 
> > 
> > I'd rather avoid that if we can. Having things named exactly the
> > same
> > as in the spec makes the code easier to read and understand.
> 
> Agreed, matching names is one of the reasons I started this work.
> 
> My thought was that the prefixed names could be fixed eventually
> after the hand-rolled code has been replaced.
> 
> 
> > What might be best is to autogenerate a header from a (combined)
> > nfs4.x, and then have the old nfs4.h file include it. Then we'd
> > just
> > have to eliminate anything from nfs4.h that conflicts with the
> > autogenerated symbols.
> > 
> > That's definitely not treating include/linux/nfs4.h as immutable,
> > but
> > we might still be able to avoid a lot of changes to the client code
> > that way.
> 
> Include the current nfs4xdr_gen.h in nfs4.h, and then remove from
> nfs4.h anything that has a naming conflict?
> 
> The downside of that is that approach mixes generated and
> hand-rolled code. Not an objection from me, but it was something
> that made Neil uncomfortable.
> 

Not exactly. The hand-rolled code would become dependent on the
generated headers, but we shouldn't need make changes to the generated
headers themselves.

> 
> > We'd need Trond and Anna to buy in on that though.
> 
> Or the client can continue to use include/linux/nfs4.h and the
> server can start using its own copy of that header.
> 

Sure, that would work too.
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 14:22   ` Jeff Layton
  2024-08-21 14:38     ` Chuck Lever
  2024-08-21 15:00     ` Christoph Hellwig
@ 2024-08-21 19:03     ` Chuck Lever
  2024-08-21 21:14       ` Chuck Lever
  2 siblings, 1 reply; 16+ messages in thread
From: Chuck Lever @ 2024-08-21 19:03 UTC (permalink / raw)
  To: Jeff Layton; +Cc: cel, linux-nfs

On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> Also, as a side note:
> 
> fs/nfsd/nfs4xdr.c: In function ‘nfsd4_encode_fattr4_open_arguments’:
> fs/nfsd/nfs4xdr.c:3446:55: error: incompatible type for argument 2 of ‘xdrgen_encode_fattr4_open_arguments’
>  3446 |         if (!xdrgen_encode_fattr4_open_arguments(xdr, &nfsd_open_arguments))
> 
> 
> OPEN_ARGUMENTS4 is a large structure with 5 different bitmaps in it. We
> probably don't want to pass that by value. When the tool is dealing
> with a struct, we should have it generate functions that take a pointer
> instead (IMO).

Meh. xdrgen already generates pass-by-reference encoders for
structs. The problem is actually this bit of nfs4_1.x:

   typedef open_arguments4 fattr4_open_arguments;

which generates:

bool
xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value)
{
        return xdrgen_encode_open_arguments4(xdr, &value);
};

So, it's a bug in the way that xdrgen handles /typedefs/ of structs,
not in the way that it handles the structs themselves.

-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-21 19:03     ` Chuck Lever
@ 2024-08-21 21:14       ` Chuck Lever
  0 siblings, 0 replies; 16+ messages in thread
From: Chuck Lever @ 2024-08-21 21:14 UTC (permalink / raw)
  To: Jeff Layton; +Cc: cel, linux-nfs

On Wed, Aug 21, 2024 at 03:03:33PM -0400, Chuck Lever wrote:
> On Wed, Aug 21, 2024 at 10:22:15AM -0400, Jeff Layton wrote:
> > Also, as a side note:
> > 
> > fs/nfsd/nfs4xdr.c: In function ‘nfsd4_encode_fattr4_open_arguments’:
> > fs/nfsd/nfs4xdr.c:3446:55: error: incompatible type for argument 2 of ‘xdrgen_encode_fattr4_open_arguments’
> >  3446 |         if (!xdrgen_encode_fattr4_open_arguments(xdr, &nfsd_open_arguments))
> > 
> > 
> > OPEN_ARGUMENTS4 is a large structure with 5 different bitmaps in it. We
> > probably don't want to pass that by value. When the tool is dealing
> > with a struct, we should have it generate functions that take a pointer
> > instead (IMO).
> 
> Meh. xdrgen already generates pass-by-reference encoders for
> structs. The problem is actually this bit of nfs4_1.x:
> 
>    typedef open_arguments4 fattr4_open_arguments;
> 
> which generates:
> 
> bool
> xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value)
> {
>         return xdrgen_encode_open_arguments4(xdr, &value);
> };
> 
> So, it's a bug in the way that xdrgen handles /typedefs/ of structs,
> not in the way that it handles the structs themselves.

I've addressed this in the lkxdrgen branch on kernel.org. I'm sure
there are still bugs ;-)


-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-20 14:46 ` [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file cel
  2024-08-21 14:22   ` Jeff Layton
@ 2024-08-22 16:34   ` Jeff Layton
  2024-08-22 17:47     ` Chuck Lever III
  1 sibling, 1 reply; 16+ messages in thread
From: Jeff Layton @ 2024-08-22 16:34 UTC (permalink / raw)
  To: cel, linux-nfs; +Cc: Chuck Lever

On Tue, 2024-08-20 at 10:46 -0400, cel@kernel.org wrote:
> From: Chuck Lever <chuck.lever@oracle.com>
> 
> Build an NFSv4 protocol snippet to support the delstid extensions.
> The new fs/nfsd/nfs4_1.x file can be added to over time as other
> parts of NFSD's XDR functions are converted to machine-generated
> code.
> 
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>  fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
>  fs/nfsd/nfs4xdr_gen.c | 236 ++++++++++++++++++++++++++++++++++++++++++
>  fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
>  3 files changed, 513 insertions(+)
>  create mode 100644 fs/nfsd/nfs4_1.x
>  create mode 100644 fs/nfsd/nfs4xdr_gen.c
>  create mode 100644 fs/nfsd/nfs4xdr_gen.h
> 
> diff --git a/fs/nfsd/nfs4_1.x b/fs/nfsd/nfs4_1.x
> new file mode 100644
> index 000000000000..d2fde450de5e
> --- /dev/null
> +++ b/fs/nfsd/nfs4_1.x
> @@ -0,0 +1,164 @@
> +/*
> + * Copyright (c) 2010 IETF Trust and the persons identified
> + * as the document authors.  All rights reserved.
> + *
> + * The document authors are identified in RFC 3530 and
> + * RFC 5661.
> + *
> + * Redistribution and use in source and binary forms, with
> + * or without modification, are permitted provided that the
> + * following conditions are met:
> + *
> + * - Redistributions of source code must retain the above
> + *   copyright notice, this list of conditions and the
> + *   following disclaimer.
> + *
> + * - Redistributions in binary form must reproduce the above
> + *   copyright notice, this list of conditions and the
> + *   following disclaimer in the documentation and/or other
> + *   materials provided with the distribution.
> + *
> + * - Neither the name of Internet Society, IETF or IETF
> + *   Trust, nor the names of specific contributors, may be
> + *   used to endorse or promote products derived from this
> + *   software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
> + *   AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
> + *   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> + *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
> + *   EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
> + *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> + *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + *   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> + *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> + *   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
> + *   IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> + *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +pragma header nfs4;
> +
> +/*
> + * Basic typedefs for RFC 1832 data type definitions
> + */
> +typedef hyper		int64_t;
> +typedef unsigned int	uint32_t;
> +
> +/*
> + * Basic data types
> + */
> +typedef uint32_t	bitmap4<>;
> +
> +/*
> + * Timeval
> + */
> +struct nfstime4 {
> +	int64_t		seconds;
> +	uint32_t	nseconds;
> +};
> +
> +
> +/*
> + * The following content was extracted from draft-ietf-nfsv4-delstid
> + */
> +
> +typedef bool            fattr4_offline;
> +
> +
> +const FATTR4_OFFLINE            = 83;
> +
> +
> +struct open_arguments4 {
> +  bitmap4  oa_share_access;
> +  bitmap4  oa_share_deny;
> +  bitmap4  oa_share_access_want;
> +  bitmap4  oa_open_claim;
> +  bitmap4  oa_create_mode;
> +};
> +
> +
> +enum open_args_share_access4 {
> +   OPEN_ARGS_SHARE_ACCESS_READ  = 1,
> +   OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
> +   OPEN_ARGS_SHARE_ACCESS_BOTH  = 3
> +};
> +
> +
> +enum open_args_share_deny4 {
> +   OPEN_ARGS_SHARE_DENY_NONE  = 0,
> +   OPEN_ARGS_SHARE_DENY_READ  = 1,
> +   OPEN_ARGS_SHARE_DENY_WRITE = 2,
> +   OPEN_ARGS_SHARE_DENY_BOTH  = 3
> +};
> +
> +
> +enum open_args_share_access_want4 {
> +   OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG           = 3,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG            = 4,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL              = 5,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL
> +                                                   = 17,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED
> +                                                   = 18,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS    = 20,
> +   OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21
> +};
> +
> +
> +enum open_args_open_claim4 {
> +   OPEN_ARGS_OPEN_CLAIM_NULL          = 0,
> +   OPEN_ARGS_OPEN_CLAIM_PREVIOUS      = 1,
> +   OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR  = 2,
> +   OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
> +   OPEN_ARGS_OPEN_CLAIM_FH            = 4,
> +   OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH  = 5,
> +   OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6
> +};
> +
> +
> +enum open_args_createmode4 {
> +   OPEN_ARGS_CREATEMODE_UNCHECKED4     = 0,
> +   OPEN_ARGS_CREATE_MODE_GUARDED       = 1,
> +   OPEN_ARGS_CREATEMODE_EXCLUSIVE4     = 2,
> +   OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1  = 3
> +};
> +
> +
> +typedef open_arguments4 fattr4_open_arguments;
> +pragma public fattr4_open_arguments;
> +
> +
> +%/*
> +% * Determine what OPEN supports.
> +% */
> +const FATTR4_OPEN_ARGUMENTS     = 86;
> +
> +
> +const OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000;
> +
> +
> +const OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010;
> +
> +
> +/*
> + * attributes for the delegation times being
> + * cached and served by the "client"
> + */
> +typedef nfstime4        fattr4_time_deleg_access;
> +typedef nfstime4        fattr4_time_deleg_modify;
> +
> +
> +%/*
> +% * New RECOMMENDED Attribute for
> +% * delegation caching of times
> +% */
> +const FATTR4_TIME_DELEG_ACCESS  = 84;
> +const FATTR4_TIME_DELEG_MODIFY  = 85;
> +
> +
> +const OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000;
> +
> diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
> new file mode 100644
> index 000000000000..bb714859d6c6
> --- /dev/null
> +++ b/fs/nfsd/nfs4xdr_gen.c
> @@ -0,0 +1,236 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Generated by xdrgen. Manual edits will be lost.
> +// XDR specification modification time: Mon Aug 19 23:28:23 2024
> +
> +#include "nfs4xdr_gen.h"
> +
> +static bool __maybe_unused
> +xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
> +{
> +	return xdrgen_decode_hyper(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
> +{
> +	return xdrgen_decode_unsigned_int(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
> +{
> +	if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
> +		return false;
> +	for (u32 i = 0; i < ptr->count; i++)
> +		if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i]))
> +			return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
> +{
> +	if (!xdrgen_decode_int64_t(xdr, &ptr->seconds))
> +		return false;
> +	if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
> +{
> +	return xdrgen_decode_bool(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr)
> +{
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_deny))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access_want))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_open_claim))
> +		return false;
> +	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 *ptr)
> +{
> +	u32 val;
> +
> +	if (xdr_stream_decode_u32(xdr, &val) < 0)
> +		return false;
> +	*ptr = val;
> +	return true;
> +}
> +
> +bool
> +xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr)
> +{
> +	return xdrgen_decode_open_arguments4(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
> +{
> +	return xdrgen_decode_nfstime4(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr)
> +{
> +	return xdrgen_decode_nfstime4(xdr, ptr);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
> +{
> +	return xdrgen_encode_hyper(xdr, value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
> +{
> +	return xdrgen_encode_unsigned_int(xdr, value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
> +{
> +	if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
> +		return false;
> +	for (u32 i = 0; i < value.count; i++)
> +		if (!xdrgen_encode_uint32_t(xdr, value.element[i]))
> +			return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
> +{
> +	if (!xdrgen_encode_int64_t(xdr, value->seconds))
> +		return false;
> +	if (!xdrgen_encode_uint32_t(xdr, value->nseconds))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
> +{
> +	return xdrgen_encode_bool(xdr, value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value)
> +{
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_deny))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access_want))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_open_claim))
> +		return false;
> +	if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode))
> +		return false;
> +	return true;
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +static bool __maybe_unused
> +xdrgen_encode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 value)
> +{
> +	return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
> +}
> +
> +bool
> +xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value)
> +{
> +	return xdrgen_encode_open_arguments4(xdr, &value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access value)
> +{
> +	return xdrgen_encode_nfstime4(xdr, &value);
> +};
> +
> +static bool __maybe_unused
> +xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify value)
> +{
> +	return xdrgen_encode_nfstime4(xdr, &value);
> +};
> diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
> new file mode 100644
> index 000000000000..27c601b36580
> --- /dev/null
> +++ b/fs/nfsd/nfs4xdr_gen.h
> @@ -0,0 +1,113 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Generated by xdrgen. Manual edits will be lost. */
> +/* XDR specification modification time: Mon Aug 19 23:28:23 2024 */
> +
> +#ifndef _LINUX_NFS4_XDRGEN_H
> +#define _LINUX_NFS4_XDRGEN_H
> +
> +#include <linux/types.h>
> +#include <linux/sunrpc/svc.h>
> +
> +#include <linux/sunrpc/xdrgen-builtins.h>
> +
> +typedef s64 int64_t;
> +
> +typedef u32 uint32_t;
> +
> +typedef struct {
> +	u32 count;
> +	uint32_t *element;
> +} bitmap4;
> +
> +struct nfstime4 {
> +	int64_t seconds;
> +	uint32_t nseconds;
> +};
> +
> +typedef bool fattr4_offline;
> +
> +enum {
> +	FATTR4_OFFLINE = 83
> +};
> +
> +struct open_arguments4 {
> +	bitmap4 oa_share_access;
> +	bitmap4 oa_share_deny;
> +	bitmap4 oa_share_access_want;
> +	bitmap4 oa_open_claim;
> +	bitmap4 oa_create_mode;
> +};
> +
> +enum open_args_share_access4 {
> +	OPEN_ARGS_SHARE_ACCESS_READ = 1,
> +	OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
> +	OPEN_ARGS_SHARE_ACCESS_BOTH = 3,
> +};
> +
> +enum open_args_share_deny4 {
> +	OPEN_ARGS_SHARE_DENY_NONE = 0,
> +	OPEN_ARGS_SHARE_DENY_READ = 1,
> +	OPEN_ARGS_SHARE_DENY_WRITE = 2,
> +	OPEN_ARGS_SHARE_DENY_BOTH = 3,
> +};
> +
> +enum open_args_share_access_want4 {
> +	OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG = 3,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG = 4,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL = 5,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 17,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 18,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20,
> +	OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21,
> +};
> +
> +enum open_args_open_claim4 {
> +	OPEN_ARGS_OPEN_CLAIM_NULL = 0,
> +	OPEN_ARGS_OPEN_CLAIM_PREVIOUS = 1,
> +	OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR = 2,
> +	OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
> +	OPEN_ARGS_OPEN_CLAIM_FH = 4,
> +	OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5,
> +	OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6,
> +};
> +
> +enum open_args_createmode4 {
> +	OPEN_ARGS_CREATEMODE_UNCHECKED4 = 0,
> +	OPEN_ARGS_CREATE_MODE_GUARDED = 1,
> +	OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2,
> +	OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3,
> +};
> +
> +typedef struct open_arguments4 fattr4_open_arguments;
> +bool xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr);
> +bool xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value);
> 

Question: why does xdrgen generate the above prototype functions for
the encoder and decoder for that typedef, but no corresponding
prototypes for the nfstime4 ones below?

> 
> +
> +enum {
> +	FATTR4_OPEN_ARGUMENTS = 86
> +};
> +
> +enum {
> +	OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000
> +};
> +
> +enum {
> +	OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010
> +};
> +
> +typedef struct nfstime4 fattr4_time_deleg_access;
> +
> +typedef struct nfstime4 fattr4_time_deleg_modify;
> +
> +enum {
> +	FATTR4_TIME_DELEG_ACCESS = 84
> +};
> +
> +enum {
> +	FATTR4_TIME_DELEG_MODIFY = 85
> +};
> +
> +enum {
> +	OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000
> +};
> +
> +#endif /* _LINUX_NFS4_XDRGEN_H */

-- 
Jeff Layton <jlayton@poochiereds.net>

-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file
  2024-08-22 16:34   ` Jeff Layton
@ 2024-08-22 17:47     ` Chuck Lever III
  0 siblings, 0 replies; 16+ messages in thread
From: Chuck Lever III @ 2024-08-22 17:47 UTC (permalink / raw)
  To: Jeff Layton; +Cc: Chuck Lever, Linux NFS Mailing List



> On Aug 22, 2024, at 12:34 PM, Jeff Layton <jlayton@kernel.org> wrote:
> 
> On Tue, 2024-08-20 at 10:46 -0400, cel@kernel.org wrote:
>> From: Chuck Lever <chuck.lever@oracle.com>
>> 
>> Build an NFSv4 protocol snippet to support the delstid extensions.
>> The new fs/nfsd/nfs4_1.x file can be added to over time as other
>> parts of NFSD's XDR functions are converted to machine-generated
>> code.
>> 
>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>> ---
>> fs/nfsd/nfs4_1.x      | 164 +++++++++++++++++++++++++++++
>> fs/nfsd/nfs4xdr_gen.c | 236 ++++++++++++++++++++++++++++++++++++++++++
>> fs/nfsd/nfs4xdr_gen.h | 113 ++++++++++++++++++++
>> 3 files changed, 513 insertions(+)
>> create mode 100644 fs/nfsd/nfs4_1.x
>> create mode 100644 fs/nfsd/nfs4xdr_gen.c
>> create mode 100644 fs/nfsd/nfs4xdr_gen.h
>> 
>> diff --git a/fs/nfsd/nfs4_1.x b/fs/nfsd/nfs4_1.x
>> new file mode 100644
>> index 000000000000..d2fde450de5e
>> --- /dev/null
>> +++ b/fs/nfsd/nfs4_1.x
>> @@ -0,0 +1,164 @@
>> +/*
>> + * Copyright (c) 2010 IETF Trust and the persons identified
>> + * as the document authors.  All rights reserved.
>> + *
>> + * The document authors are identified in RFC 3530 and
>> + * RFC 5661.
>> + *
>> + * Redistribution and use in source and binary forms, with
>> + * or without modification, are permitted provided that the
>> + * following conditions are met:
>> + *
>> + * - Redistributions of source code must retain the above
>> + *   copyright notice, this list of conditions and the
>> + *   following disclaimer.
>> + *
>> + * - Redistributions in binary form must reproduce the above
>> + *   copyright notice, this list of conditions and the
>> + *   following disclaimer in the documentation and/or other
>> + *   materials provided with the distribution.
>> + *
>> + * - Neither the name of Internet Society, IETF or IETF
>> + *   Trust, nor the names of specific contributors, may be
>> + *   used to endorse or promote products derived from this
>> + *   software without specific prior written permission.
>> + *
>> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
>> + *   AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
>> + *   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
>> + *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
>> + *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
>> + *   EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
>> + *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
>> + *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
>> + *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
>> + *   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
>> + *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
>> + *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>> + *   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
>> + *   IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
>> + *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>> + */
>> +
>> +pragma header nfs4;
>> +
>> +/*
>> + * Basic typedefs for RFC 1832 data type definitions
>> + */
>> +typedef hyper int64_t;
>> +typedef unsigned int uint32_t;
>> +
>> +/*
>> + * Basic data types
>> + */
>> +typedef uint32_t bitmap4<>;
>> +
>> +/*
>> + * Timeval
>> + */
>> +struct nfstime4 {
>> + int64_t seconds;
>> + uint32_t nseconds;
>> +};
>> +
>> +
>> +/*
>> + * The following content was extracted from draft-ietf-nfsv4-delstid
>> + */
>> +
>> +typedef bool            fattr4_offline;
>> +
>> +
>> +const FATTR4_OFFLINE            = 83;
>> +
>> +
>> +struct open_arguments4 {
>> +  bitmap4  oa_share_access;
>> +  bitmap4  oa_share_deny;
>> +  bitmap4  oa_share_access_want;
>> +  bitmap4  oa_open_claim;
>> +  bitmap4  oa_create_mode;
>> +};
>> +
>> +
>> +enum open_args_share_access4 {
>> +   OPEN_ARGS_SHARE_ACCESS_READ  = 1,
>> +   OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
>> +   OPEN_ARGS_SHARE_ACCESS_BOTH  = 3
>> +};
>> +
>> +
>> +enum open_args_share_deny4 {
>> +   OPEN_ARGS_SHARE_DENY_NONE  = 0,
>> +   OPEN_ARGS_SHARE_DENY_READ  = 1,
>> +   OPEN_ARGS_SHARE_DENY_WRITE = 2,
>> +   OPEN_ARGS_SHARE_DENY_BOTH  = 3
>> +};
>> +
>> +
>> +enum open_args_share_access_want4 {
>> +   OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG           = 3,
>> +   OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG            = 4,
>> +   OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL              = 5,
>> +   OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL
>> +                                                   = 17,
>> +   OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED
>> +                                                   = 18,
>> +   OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS    = 20,
>> +   OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21
>> +};
>> +
>> +
>> +enum open_args_open_claim4 {
>> +   OPEN_ARGS_OPEN_CLAIM_NULL          = 0,
>> +   OPEN_ARGS_OPEN_CLAIM_PREVIOUS      = 1,
>> +   OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR  = 2,
>> +   OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
>> +   OPEN_ARGS_OPEN_CLAIM_FH            = 4,
>> +   OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH  = 5,
>> +   OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6
>> +};
>> +
>> +
>> +enum open_args_createmode4 {
>> +   OPEN_ARGS_CREATEMODE_UNCHECKED4     = 0,
>> +   OPEN_ARGS_CREATE_MODE_GUARDED       = 1,
>> +   OPEN_ARGS_CREATEMODE_EXCLUSIVE4     = 2,
>> +   OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1  = 3
>> +};
>> +
>> +
>> +typedef open_arguments4 fattr4_open_arguments;
>> +pragma public fattr4_open_arguments;
>> +
>> +
>> +%/*
>> +% * Determine what OPEN supports.
>> +% */
>> +const FATTR4_OPEN_ARGUMENTS     = 86;
>> +
>> +
>> +const OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000;
>> +
>> +
>> +const OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010;
>> +
>> +
>> +/*
>> + * attributes for the delegation times being
>> + * cached and served by the "client"
>> + */
>> +typedef nfstime4        fattr4_time_deleg_access;
>> +typedef nfstime4        fattr4_time_deleg_modify;
>> +
>> +
>> +%/*
>> +% * New RECOMMENDED Attribute for
>> +% * delegation caching of times
>> +% */
>> +const FATTR4_TIME_DELEG_ACCESS  = 84;
>> +const FATTR4_TIME_DELEG_MODIFY  = 85;
>> +
>> +
>> +const OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000;
>> +
>> diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
>> new file mode 100644
>> index 000000000000..bb714859d6c6
>> --- /dev/null
>> +++ b/fs/nfsd/nfs4xdr_gen.c
>> @@ -0,0 +1,236 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Generated by xdrgen. Manual edits will be lost.
>> +// XDR specification modification time: Mon Aug 19 23:28:23 2024
>> +
>> +#include "nfs4xdr_gen.h"
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
>> +{
>> + return xdrgen_decode_hyper(xdr, ptr);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
>> +{
>> + return xdrgen_decode_unsigned_int(xdr, ptr);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
>> +{
>> + if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
>> + return false;
>> + for (u32 i = 0; i < ptr->count; i++)
>> + if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i]))
>> + return false;
>> + return true;
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
>> +{
>> + if (!xdrgen_decode_int64_t(xdr, &ptr->seconds))
>> + return false;
>> + if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds))
>> + return false;
>> + return true;
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
>> +{
>> + return xdrgen_decode_bool(xdr, ptr);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr)
>> +{
>> + if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access))
>> + return false;
>> + if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_deny))
>> + return false;
>> + if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_share_access_want))
>> + return false;
>> + if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_open_claim))
>> + return false;
>> + if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode))
>> + return false;
>> + return true;
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 *ptr)
>> +{
>> + u32 val;
>> +
>> + if (xdr_stream_decode_u32(xdr, &val) < 0)
>> + return false;
>> + *ptr = val;
>> + return true;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 *ptr)
>> +{
>> + u32 val;
>> +
>> + if (xdr_stream_decode_u32(xdr, &val) < 0)
>> + return false;
>> + *ptr = val;
>> + return true;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 *ptr)
>> +{
>> + u32 val;
>> +
>> + if (xdr_stream_decode_u32(xdr, &val) < 0)
>> + return false;
>> + *ptr = val;
>> + return true;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 *ptr)
>> +{
>> + u32 val;
>> +
>> + if (xdr_stream_decode_u32(xdr, &val) < 0)
>> + return false;
>> + *ptr = val;
>> + return true;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 *ptr)
>> +{
>> + u32 val;
>> +
>> + if (xdr_stream_decode_u32(xdr, &val) < 0)
>> + return false;
>> + *ptr = val;
>> + return true;
>> +}
>> +
>> +bool
>> +xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr)
>> +{
>> + return xdrgen_decode_open_arguments4(xdr, ptr);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
>> +{
>> + return xdrgen_decode_nfstime4(xdr, ptr);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr)
>> +{
>> + return xdrgen_decode_nfstime4(xdr, ptr);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
>> +{
>> + return xdrgen_encode_hyper(xdr, value);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
>> +{
>> + return xdrgen_encode_unsigned_int(xdr, value);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
>> +{
>> + if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
>> + return false;
>> + for (u32 i = 0; i < value.count; i++)
>> + if (!xdrgen_encode_uint32_t(xdr, value.element[i]))
>> + return false;
>> + return true;
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
>> +{
>> + if (!xdrgen_encode_int64_t(xdr, value->seconds))
>> + return false;
>> + if (!xdrgen_encode_uint32_t(xdr, value->nseconds))
>> + return false;
>> + return true;
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
>> +{
>> + return xdrgen_encode_bool(xdr, value);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value)
>> +{
>> + if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access))
>> + return false;
>> + if (!xdrgen_encode_bitmap4(xdr, value->oa_share_deny))
>> + return false;
>> + if (!xdrgen_encode_bitmap4(xdr, value->oa_share_access_want))
>> + return false;
>> + if (!xdrgen_encode_bitmap4(xdr, value->oa_open_claim))
>> + return false;
>> + if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode))
>> + return false;
>> + return true;
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, enum open_args_share_access4 value)
>> +{
>> + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_open_args_share_deny4(struct xdr_stream *xdr, enum open_args_share_deny4 value)
>> +{
>> + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_open_args_share_access_want4(struct xdr_stream *xdr, enum open_args_share_access_want4 value)
>> +{
>> + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_open_args_open_claim4(struct xdr_stream *xdr, enum open_args_open_claim4 value)
>> +{
>> + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
>> +}
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_open_args_createmode4(struct xdr_stream *xdr, enum open_args_createmode4 value)
>> +{
>> + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
>> +}
>> +
>> +bool
>> +xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value)
>> +{
>> + return xdrgen_encode_open_arguments4(xdr, &value);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access value)
>> +{
>> + return xdrgen_encode_nfstime4(xdr, &value);
>> +};
>> +
>> +static bool __maybe_unused
>> +xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify value)
>> +{
>> + return xdrgen_encode_nfstime4(xdr, &value);
>> +};
>> diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
>> new file mode 100644
>> index 000000000000..27c601b36580
>> --- /dev/null
>> +++ b/fs/nfsd/nfs4xdr_gen.h
>> @@ -0,0 +1,113 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Generated by xdrgen. Manual edits will be lost. */
>> +/* XDR specification modification time: Mon Aug 19 23:28:23 2024 */
>> +
>> +#ifndef _LINUX_NFS4_XDRGEN_H
>> +#define _LINUX_NFS4_XDRGEN_H
>> +
>> +#include <linux/types.h>
>> +#include <linux/sunrpc/svc.h>
>> +
>> +#include <linux/sunrpc/xdrgen-builtins.h>
>> +
>> +typedef s64 int64_t;
>> +
>> +typedef u32 uint32_t;
>> +
>> +typedef struct {
>> + u32 count;
>> + uint32_t *element;
>> +} bitmap4;
>> +
>> +struct nfstime4 {
>> + int64_t seconds;
>> + uint32_t nseconds;
>> +};
>> +
>> +typedef bool fattr4_offline;
>> +
>> +enum {
>> + FATTR4_OFFLINE = 83
>> +};
>> +
>> +struct open_arguments4 {
>> + bitmap4 oa_share_access;
>> + bitmap4 oa_share_deny;
>> + bitmap4 oa_share_access_want;
>> + bitmap4 oa_open_claim;
>> + bitmap4 oa_create_mode;
>> +};
>> +
>> +enum open_args_share_access4 {
>> + OPEN_ARGS_SHARE_ACCESS_READ = 1,
>> + OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
>> + OPEN_ARGS_SHARE_ACCESS_BOTH = 3,
>> +};
>> +
>> +enum open_args_share_deny4 {
>> + OPEN_ARGS_SHARE_DENY_NONE = 0,
>> + OPEN_ARGS_SHARE_DENY_READ = 1,
>> + OPEN_ARGS_SHARE_DENY_WRITE = 2,
>> + OPEN_ARGS_SHARE_DENY_BOTH = 3,
>> +};
>> +
>> +enum open_args_share_access_want4 {
>> + OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG = 3,
>> + OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG = 4,
>> + OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL = 5,
>> + OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 17,
>> + OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 18,
>> + OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20,
>> + OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21,
>> +};
>> +
>> +enum open_args_open_claim4 {
>> + OPEN_ARGS_OPEN_CLAIM_NULL = 0,
>> + OPEN_ARGS_OPEN_CLAIM_PREVIOUS = 1,
>> + OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR = 2,
>> + OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV = 3,
>> + OPEN_ARGS_OPEN_CLAIM_FH = 4,
>> + OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5,
>> + OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6,
>> +};
>> +
>> +enum open_args_createmode4 {
>> + OPEN_ARGS_CREATEMODE_UNCHECKED4 = 0,
>> + OPEN_ARGS_CREATE_MODE_GUARDED = 1,
>> + OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2,
>> + OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3,
>> +};
>> +
>> +typedef struct open_arguments4 fattr4_open_arguments;
>> +bool xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr);
>> +bool xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments value);
>> 
> 
> Question: why does xdrgen generate the above prototype functions for
> the encoder and decoder for that typedef, but no corresponding
> prototypes for the nfstime4 ones below?

Because I added a "pragma public" in the .x file for
the fattr4_open_arguments type.

You can add a "pragma public" for nfstime4 as well.
Have a look in the README for examples.

But I don't think you need one unless you have code
that wants to call xdrgen_encode_nfstime4().


>> +
>> +enum {
>> + FATTR4_OPEN_ARGUMENTS = 86
>> +};
>> +
>> +enum {
>> + OPEN4_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 0x200000
>> +};
>> +
>> +enum {
>> + OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010
>> +};
>> +
>> +typedef struct nfstime4 fattr4_time_deleg_access;
>> +
>> +typedef struct nfstime4 fattr4_time_deleg_modify;
>> +
>> +enum {
>> + FATTR4_TIME_DELEG_ACCESS = 84
>> +};
>> +
>> +enum {
>> + FATTR4_TIME_DELEG_MODIFY = 85
>> +};
>> +
>> +enum {
>> + OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 0x100000
>> +};
>> +
>> +#endif /* _LINUX_NFS4_XDRGEN_H */
> 
> -- 
> Jeff Layton <jlayton@poochiereds.net>
> 
> -- 
> Jeff Layton <jlayton@kernel.org>


--
Chuck Lever



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2024-08-22 17:48 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-20 14:45 [RFC PATCH 0/2] xdrgen - machine-generated XDR functions cel
2024-08-20 14:45 ` [RFC PATCH 1/2] tools: Add xdrgen cel
2024-08-20 14:46 ` [RFC PATCH 2/2] NFSD: Create an initial nfs4_1.x file cel
2024-08-21 14:22   ` Jeff Layton
2024-08-21 14:38     ` Chuck Lever
2024-08-21 16:51       ` Jeff Layton
2024-08-21 17:03         ` Chuck Lever
2024-08-21 17:34           ` Jeff Layton
2024-08-21 15:00     ` Christoph Hellwig
2024-08-21 15:59       ` Jeff Layton
2024-08-21 19:03     ` Chuck Lever
2024-08-21 21:14       ` Chuck Lever
2024-08-22 16:34   ` Jeff Layton
2024-08-22 17:47     ` Chuck Lever III
2024-08-21 14:03 ` [RFC PATCH 0/2] xdrgen - machine-generated XDR functions Jeff Layton
2024-08-21 14:06   ` Chuck Lever III

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).