mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 00:53:34 +02:00
struct svc_service has a .vs_xdrsize field that is filled in by
servers for each of their RPC programs. This field is supposed to
contain the size of the largest procedure argument in the RPC
program. This value is also sometimes used to size network
transport buffers.
Currently, server implementations must manually calculate and
hard-code this value, which is error-prone and requires updates
when procedure arguments change.
Update xdrgen to determine which procedure argument structure is
largest, and emit a macro with a well-known name that contains
the size of that structure. Server code then uses this macro when
initializing the .vs_xdrsize field.
For NLM version 4, xdrgen now emits:
#define NLM4_MAX_ARGS_SZ (NLM4_nlm4_lockargs_sz)
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
205 lines
7.6 KiB
Python
205 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
# ex: set filetype=python:
|
|
|
|
"""Generate code for an RPC program's procedures"""
|
|
|
|
from jinja2 import Environment
|
|
|
|
from generators import SourceGenerator, create_jinja2_environment, get_jinja2_template
|
|
from xdr_ast import _RpcProgram, _RpcVersion, excluded_apis
|
|
from xdr_ast import max_widths, get_header_name
|
|
|
|
|
|
def emit_version_definitions(
|
|
environment: Environment, program: str, version: _RpcVersion
|
|
) -> None:
|
|
"""Emit procedure numbers for each RPC version's procedures"""
|
|
template = environment.get_template("definition/open.j2")
|
|
print(template.render(program=program.upper()))
|
|
|
|
template = environment.get_template("definition/procedure.j2")
|
|
for procedure in version.procedures:
|
|
if procedure.name not in excluded_apis:
|
|
print(
|
|
template.render(
|
|
name=procedure.name,
|
|
value=procedure.number,
|
|
)
|
|
)
|
|
|
|
template = environment.get_template("definition/close.j2")
|
|
print(template.render())
|
|
|
|
|
|
def emit_version_declarations(
|
|
environment: Environment, program: str, version: _RpcVersion
|
|
) -> None:
|
|
"""Emit declarations for each RPC version's procedures"""
|
|
arguments = dict.fromkeys([])
|
|
for procedure in version.procedures:
|
|
if procedure.name not in excluded_apis:
|
|
arguments[procedure.argument.type_name] = None
|
|
if len(arguments) > 0:
|
|
print("")
|
|
template = environment.get_template("declaration/argument.j2")
|
|
for argument in arguments:
|
|
print(template.render(program=program, argument=argument))
|
|
|
|
results = dict.fromkeys([])
|
|
for procedure in version.procedures:
|
|
if procedure.name not in excluded_apis:
|
|
results[procedure.result.type_name] = None
|
|
if len(results) > 0:
|
|
print("")
|
|
template = environment.get_template("declaration/result.j2")
|
|
for result in results:
|
|
print(template.render(program=program, result=result))
|
|
|
|
|
|
def emit_version_argument_decoders(
|
|
environment: Environment, program: str, version: _RpcVersion
|
|
) -> None:
|
|
"""Emit server argument decoders for each RPC version's procedures"""
|
|
arguments = dict.fromkeys([])
|
|
for procedure in version.procedures:
|
|
if procedure.name not in excluded_apis:
|
|
arguments[procedure.argument.type_name] = None
|
|
|
|
template = environment.get_template("decoder/argument.j2")
|
|
for argument in arguments:
|
|
print(template.render(program=program, argument=argument))
|
|
|
|
|
|
def emit_version_result_decoders(
|
|
environment: Environment, program: str, version: _RpcVersion
|
|
) -> None:
|
|
"""Emit client result decoders for each RPC version's procedures"""
|
|
results = dict.fromkeys([])
|
|
for procedure in version.procedures:
|
|
if procedure.name not in excluded_apis:
|
|
results[procedure.result.type_name] = None
|
|
|
|
template = environment.get_template("decoder/result.j2")
|
|
for result in results:
|
|
print(template.render(program=program, result=result))
|
|
|
|
|
|
def emit_version_argument_encoders(
|
|
environment: Environment, program: str, version: _RpcVersion
|
|
) -> None:
|
|
"""Emit client argument encoders for each RPC version's procedures"""
|
|
arguments = dict.fromkeys([])
|
|
for procedure in version.procedures:
|
|
if procedure.name not in excluded_apis:
|
|
arguments[procedure.argument.type_name] = None
|
|
|
|
template = environment.get_template("encoder/argument.j2")
|
|
for argument in arguments:
|
|
print(template.render(program=program, argument=argument))
|
|
|
|
|
|
def emit_version_result_encoders(
|
|
environment: Environment, program: str, version: _RpcVersion
|
|
) -> None:
|
|
"""Emit server result encoders for each RPC version's procedures"""
|
|
results = dict.fromkeys([])
|
|
for procedure in version.procedures:
|
|
if procedure.name not in excluded_apis:
|
|
results[procedure.result.type_name] = None
|
|
|
|
template = environment.get_template("encoder/result.j2")
|
|
for result in results:
|
|
print(template.render(program=program, result=result))
|
|
|
|
|
|
class XdrProgramGenerator(SourceGenerator):
|
|
"""Generate source code for an RPC program's procedures"""
|
|
|
|
def __init__(self, language: str, peer: str):
|
|
"""Initialize an instance of this class"""
|
|
self.environment = create_jinja2_environment(language, "program")
|
|
self.peer = peer
|
|
|
|
def emit_definition(self, node: _RpcProgram) -> None:
|
|
"""Emit procedure numbers for each of an RPC programs's procedures"""
|
|
raw_name = node.name
|
|
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
|
|
|
for version in node.versions:
|
|
emit_version_definitions(self.environment, program, version)
|
|
|
|
template = self.environment.get_template("definition/program.j2")
|
|
print(template.render(name=raw_name, value=node.number))
|
|
|
|
def emit_declaration(self, node: _RpcProgram) -> None:
|
|
"""Emit a declaration pair for each of an RPC programs's procedures"""
|
|
raw_name = node.name
|
|
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
|
|
|
for version in node.versions:
|
|
emit_version_declarations(self.environment, program, version)
|
|
|
|
def emit_decoder(self, node: _RpcProgram) -> None:
|
|
"""Emit all decoder functions for an RPC program's procedures"""
|
|
raw_name = node.name
|
|
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
|
match self.peer:
|
|
case "server":
|
|
for version in node.versions:
|
|
emit_version_argument_decoders(
|
|
self.environment, program, version,
|
|
)
|
|
case "client":
|
|
for version in node.versions:
|
|
emit_version_result_decoders(
|
|
self.environment, program, version,
|
|
)
|
|
|
|
def emit_encoder(self, node: _RpcProgram) -> None:
|
|
"""Emit all encoder functions for an RPC program's procedures"""
|
|
raw_name = node.name
|
|
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
|
match self.peer:
|
|
case "server":
|
|
for version in node.versions:
|
|
emit_version_result_encoders(
|
|
self.environment, program, version,
|
|
)
|
|
case "client":
|
|
for version in node.versions:
|
|
emit_version_argument_encoders(
|
|
self.environment, program, version,
|
|
)
|
|
|
|
def emit_maxsize(self, node: _RpcProgram) -> None:
|
|
"""Emit maxsize macro for maximum RPC argument size"""
|
|
header = get_header_name().upper()
|
|
|
|
# Find the largest argument across all versions
|
|
max_arg_width = 0
|
|
max_arg_name = None
|
|
for version in node.versions:
|
|
for procedure in version.procedures:
|
|
if procedure.name in excluded_apis:
|
|
continue
|
|
arg_name = procedure.argument.type_name
|
|
if arg_name == "void":
|
|
continue
|
|
if arg_name not in max_widths:
|
|
continue
|
|
if max_widths[arg_name] > max_arg_width:
|
|
max_arg_width = max_widths[arg_name]
|
|
max_arg_name = arg_name
|
|
|
|
if max_arg_name is None:
|
|
return
|
|
|
|
macro_name = header + "_MAX_ARGS_SZ"
|
|
template = get_jinja2_template(self.environment, "maxsize", "max_args")
|
|
print(
|
|
template.render(
|
|
macro=macro_name,
|
|
width=header + "_" + max_arg_name + "_sz",
|
|
)
|
|
)
|