* [PATCH v2 2/9] kernel/api: enable kerneldoc-based API specifications
From: Sasha Levin @ 2026-03-22 12:10 UTC (permalink / raw)
To: linux-api, linux-kernel
Cc: linux-doc, linux-fsdevel, linux-kbuild, linux-kselftest,
workflows, tools, x86, Thomas Gleixner, Paul E . McKenney,
Greg Kroah-Hartman, Jonathan Corbet, Dmitry Vyukov, Randy Dunlap,
Cyril Hrubis, Kees Cook, Jake Edge, David Laight, Askar Safin,
Gabriele Paoloni, Mauro Carvalho Chehab, Christian Brauner,
Alexander Viro, Andrew Morton, Masahiro Yamada, Shuah Khan,
Ingo Molnar, Arnd Bergmann, Sasha Levin
In-Reply-To: <20260322121026.869758-1-sashal@kernel.org>
This patch adds support for extracting API specifications from
kernel-doc comments and generating C macro invocations for the
kernel API specification framework.
Changes include:
- New kdoc_apispec.py module for generating API spec macros
- Updates to kernel-doc.py to support -apispec output format
- Build system integration in Makefile.build
- Generator script for collecting all API specifications
- Support for API-specific sections in kernel-doc comments
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
scripts/Makefile.build | 31 +
scripts/Makefile.clean | 3 +
scripts/generate_api_specs.sh | 18 -
tools/docs/kernel-doc | 5 +
tools/lib/python/kdoc/kdoc_apispec.py | 888 ++++++++++++++++++++++++++
tools/lib/python/kdoc/kdoc_output.py | 9 +-
tools/lib/python/kdoc/kdoc_parser.py | 85 ++-
7 files changed, 1016 insertions(+), 23 deletions(-)
delete mode 100755 scripts/generate_api_specs.sh
create mode 100644 tools/lib/python/kdoc/kdoc_apispec.py
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 3652b85be5459..ef203e490c797 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -174,6 +174,37 @@ ifneq ($(KBUILD_EXTRA_WARN),)
endif
endif
+# Generate API spec headers from kernel-doc comments
+ifeq ($(CONFIG_KAPI_SPEC),y)
+# Function to check if a file has API specifications
+has-apispec = $(shell grep -qE '^\s*\*\s*context-flags:' $(src)/$(1) 2>/dev/null && echo $(1))
+
+# Get base names without directory prefix
+c-objs-base := $(notdir $(real-obj-y) $(real-obj-m))
+# Filter to only .o files with corresponding .c source files
+c-files := $(foreach o,$(c-objs-base),$(if $(wildcard $(src)/$(o:.o=.c)),$(o:.o=.c)))
+# Also check for any additional .c files that contain API specs but are included
+extra-c-files := $(shell find $(src) -maxdepth 1 -name "*.c" -exec grep -l '^\s*\*\s*\(long-desc\|context-flags\|state-trans\):' {} \; 2>/dev/null | xargs -r basename -a)
+# Combine both lists and remove duplicates
+all-c-files := $(sort $(c-files) $(extra-c-files))
+# Only include files that actually have API specifications
+apispec-files := $(foreach f,$(all-c-files),$(call has-apispec,$(f)))
+# Generate apispec targets with proper directory prefix
+apispec-y := $(addprefix $(obj)/,$(apispec-files:.c=.apispec.h))
+always-y += $(apispec-y)
+targets += $(apispec-y)
+
+quiet_cmd_apispec = APISPEC $@
+ cmd_apispec = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -apispec \
+ $(KDOCFLAGS) $< > $@ || rm -f $@
+
+$(obj)/%.apispec.h: $(src)/%.c $(KERNELDOC) FORCE
+ $(call if_changed,apispec)
+
+# Source files that include their own apispec.h need to depend on it
+$(foreach f,$(apispec-files),$(eval $(obj)/$(f:.c=.o): $(obj)/$(f:.c=.apispec.h)))
+endif
+
# Compile C sources (.c)
# ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean
index 6ead00ec7313b..f78dbbe637f27 100644
--- a/scripts/Makefile.clean
+++ b/scripts/Makefile.clean
@@ -35,6 +35,9 @@ __clean-files := $(filter-out $(no-clean-files), $(__clean-files))
__clean-files := $(wildcard $(addprefix $(obj)/, $(__clean-files)))
+# Also clean generated apispec headers (computed dynamically in Makefile.build)
+__clean-files += $(wildcard $(obj)/*.apispec.h)
+
# ==========================================================================
# To make this rule robust against "Argument list too long" error,
diff --git a/scripts/generate_api_specs.sh b/scripts/generate_api_specs.sh
deleted file mode 100755
index 2c3078a508fef..0000000000000
--- a/scripts/generate_api_specs.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# Stub script for generating API specifications collector
-# This is a placeholder until the full implementation is available
-#
-
-cat << 'EOF'
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Auto-generated API specifications collector (stub)
- * Generated by scripts/generate_api_specs.sh
- */
-
-#include <linux/kernel_api_spec.h>
-
-/* No API specifications collected yet */
-EOF
diff --git a/tools/docs/kernel-doc b/tools/docs/kernel-doc
index aed09f9a54dd1..e71e663d9b7c0 100755
--- a/tools/docs/kernel-doc
+++ b/tools/docs/kernel-doc
@@ -253,6 +253,8 @@ def main():
help="Output reStructuredText format (default).")
out_fmt.add_argument("-N", "-none", "--none", action="store_true",
help="Do not output documentation, only warnings.")
+ out_fmt.add_argument("-apispec", "--apispec", action="store_true",
+ help="Output C macro invocations for kernel API specifications.")
#
# Output selection mutually-exclusive group
@@ -323,11 +325,14 @@ def main():
#
from kdoc.kdoc_files import KernelFiles # pylint: disable=C0415
from kdoc.kdoc_output import RestFormat, ManFormat # pylint: disable=C0415
+ from kdoc.kdoc_apispec import ApiSpecFormat # pylint: disable=C0415
if args.man:
out_style = ManFormat(modulename=args.modulename)
elif args.none:
out_style = None
+ elif args.apispec:
+ out_style = ApiSpecFormat()
else:
out_style = RestFormat()
diff --git a/tools/lib/python/kdoc/kdoc_apispec.py b/tools/lib/python/kdoc/kdoc_apispec.py
new file mode 100644
index 0000000000000..1279184f48143
--- /dev/null
+++ b/tools/lib/python/kdoc/kdoc_apispec.py
@@ -0,0 +1,888 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+
+"""
+Generate C macro invocations for kernel API specifications from kernel-doc comments.
+
+This module creates C header files with API specification macros that match
+the kernel API specification framework introduced in commit 9688de5c25bed.
+"""
+
+from kdoc.kdoc_output import OutputFormat
+import re
+
+
+# Maximum string lengths (from kernel_api_spec.h)
+KAPI_MAX_DESC_LEN = 512
+KAPI_MAX_NAME_LEN = 128
+KAPI_MAX_SIGNAL_NAME_LEN = 32
+KAPI_MAX_LONG_DESC_LEN = KAPI_MAX_DESC_LEN * 4 # 2048 bytes
+KAPI_MAX_NOTES_LEN = KAPI_MAX_DESC_LEN * 2 # 1024 bytes
+KAPI_MAX_EXAMPLES_LEN = KAPI_MAX_DESC_LEN * 2 # 1024 bytes
+
+# Valid KAPI effect types
+VALID_EFFECT_TYPES = {
+ 'KAPI_EFFECT_NONE', 'KAPI_EFFECT_MODIFY_STATE', 'KAPI_EFFECT_PROCESS_STATE',
+ 'KAPI_EFFECT_IRREVERSIBLE', 'KAPI_EFFECT_SCHEDULE', 'KAPI_EFFECT_FILESYSTEM',
+ 'KAPI_EFFECT_HARDWARE', 'KAPI_EFFECT_ALLOC_MEMORY', 'KAPI_EFFECT_FREE_MEMORY',
+ 'KAPI_EFFECT_SIGNAL_SEND', 'KAPI_EFFECT_FILE_POSITION', 'KAPI_EFFECT_LOCK_ACQUIRE',
+ 'KAPI_EFFECT_LOCK_RELEASE', 'KAPI_EFFECT_RESOURCE_CREATE', 'KAPI_EFFECT_RESOURCE_DESTROY',
+ 'KAPI_EFFECT_NETWORK'
+}
+
+
+class ApiSpecFormat(OutputFormat):
+ """Generate C macro invocations for kernel API specifications"""
+
+ def __init__(self):
+ super().__init__()
+ self.header_written = False
+
+ def msg(self, fname, name, args):
+ """Handles a single entry from kernel-doc parser"""
+ if not self.header_written:
+ header = self._generate_header()
+ self.header_written = True
+ else:
+ header = ""
+
+ self.data = ""
+ result = super().msg(fname, name, args)
+ return header + (result if result else self.data)
+
+ def _generate_header(self):
+ """Generate the file header"""
+ return (
+ "/* SPDX-License-Identifier: GPL-2.0 */\n"
+ "/* Auto-generated from kerneldoc annotations - DO NOT EDIT */\n\n"
+ "#include <linux/kernel_api_spec.h>\n"
+ "#include <linux/errno.h>\n\n"
+ )
+
+ def _format_macro_param(self, value, max_len=KAPI_MAX_DESC_LEN):
+ """Format a value for use in C macro parameter, truncating if needed"""
+ if value is None:
+ return '""'
+ value = str(value).replace('\\', '\\\\').replace('"', '\\"')
+ value = value.replace('\n', ' ').replace('\t', ' ').replace('\r', '')
+ value = value.replace('\0', '')
+ # Truncate to fit within max_len, accounting for null terminator
+ if len(value) > max_len - 1:
+ value = value[:max_len - 4] + '...'
+ return f'"{value}"'
+
+ def _get_section(self, sections, key):
+ """Get first line from sections, checking with and without @ prefix and case variants"""
+ for variant in [key, key.capitalize(), key.title()]:
+ for prefix in ['', '@']:
+ full_key = prefix + variant
+ if full_key in sections:
+ content = sections[full_key].strip()
+ # Return only first line to avoid mixing sections
+ return content.split('\n')[0].strip() if content else ''
+ return None
+
+ def _get_raw_section(self, sections, key):
+ """Get full section content, checking with and without @ prefix and case variants"""
+ for variant in [key, key.capitalize(), key.title()]:
+ for prefix in ['', '@']:
+ full_key = prefix + variant
+ if full_key in sections:
+ return sections[full_key]
+ return ''
+
+ def _get_multiline_section(self, sections, key):
+ """Get full multi-line section content, joined into a single string.
+
+ This is used for fields like notes, long-desc, and examples that
+ can span multiple lines in the kerneldoc comment.
+ """
+ content = self._get_raw_section(sections, key)
+ if not content:
+ return None
+
+ # Split into lines, strip each, and join with space
+ lines = content.strip().split('\n')
+ # Join lines, preserving paragraph breaks (double newlines become single space)
+ result = ' '.join(line.strip() for line in lines if line.strip())
+ return result if result else None
+
+ def _parse_indented_items(self, section_content, item_parser):
+ """Generic parser for indented items.
+
+ Args:
+ section_content: Raw section content
+ item_parser: Function that takes (lines, start_index) and returns (item, next_index)
+
+ Returns:
+ List of parsed items
+ """
+ if not section_content:
+ return []
+
+ items = []
+ lines = section_content.strip().split('\n')
+ i = 0
+
+ while i < len(lines):
+ if not lines[i].strip():
+ i += 1
+ continue
+
+ # Check if this is a main item (not indented)
+ if not lines[i].startswith((' ', '\t')):
+ item, i = item_parser(lines, i)
+ if item:
+ items.append(item)
+ else:
+ i += 1
+
+ return items
+
+ def _parse_subfields(self, lines, start_idx):
+ """Parse indented subfields starting from start_idx+1.
+
+ Returns: (dict of subfields, next index)
+ """
+ subfields = {}
+ i = start_idx + 1
+
+ while i < len(lines) and (lines[i].startswith((' ', '\t'))):
+ line = lines[i].strip()
+ if ':' in line:
+ key, value = line.split(':', 1)
+ subfields[key.strip()] = value.strip()
+ i += 1
+
+ return subfields, i
+
+ def _parse_signal_item(self, lines, i):
+ """Parse a single signal specification"""
+ signal = {'name': lines[i].strip()}
+ subfields, next_i = self._parse_subfields(lines, i)
+
+ # Map subfields to signal attributes
+ signal.update({
+ 'direction': subfields.get('direction', 'KAPI_SIGNAL_RECEIVE'),
+ 'action': subfields.get('action', 'KAPI_SIGNAL_ACTION_RETURN'),
+ 'condition': subfields.get('condition'),
+ 'desc': subfields.get('desc'),
+ 'error': subfields.get('error'),
+ 'timing': subfields.get('timing'),
+ 'priority': subfields.get('priority'),
+ 'interruptible': subfields.get('interruptible', '').lower() == 'yes',
+ 'number': subfields.get('number', '0'),
+ })
+
+ return signal, next_i
+
+ def _parse_error_item(self, lines, i):
+ """Parse a single error specification"""
+ line = lines[i].strip()
+
+ # Skip desc: lines
+ if line.startswith('desc:'):
+ return None, i + 1
+
+ # Check for error pattern
+ if not re.match(r'^[A-Z][A-Z0-9_]+,', line):
+ return None, i + 1
+
+ error = {'line': line, 'desc': ''}
+
+ # Look for desc: continuation
+ i += 1
+ desc_lines = []
+ while i < len(lines):
+ next_line = lines[i].strip()
+ if next_line.startswith('desc:'):
+ desc_lines.append(next_line[5:].strip())
+ i += 1
+ elif not next_line:
+ break
+ elif not desc_lines and re.match(r'^[A-Z][A-Z0-9_]+,', next_line):
+ # New error entry, but only if we haven't started a desc block
+ break
+ else:
+ desc_lines.append(next_line)
+ i += 1
+
+ if desc_lines:
+ error['desc'] = ' '.join(desc_lines)
+
+ return error, i
+
+ def _parse_lock_item(self, lines, i):
+ """Parse a single lock specification"""
+ line = lines[i].strip()
+ if ':' not in line:
+ return None, i + 1
+
+ parts = line.split(':', 1)[1].strip().split(',', 1)
+ if len(parts) < 2:
+ return None, i + 1
+
+ lock = {
+ 'name': parts[0].strip(),
+ 'type': parts[1].strip()
+ }
+
+ subfields, next_i = self._parse_subfields(lines, i)
+
+ # Map boolean fields
+ for field in ['acquired', 'released', 'held-on-entry', 'held-on-exit']:
+ if subfields.get(field, '').lower() == 'true':
+ lock[field] = True
+
+ lock['desc'] = subfields.get('desc', '')
+
+ return lock, next_i
+
+ def _parse_constraint_item(self, lines, i):
+ """Parse a single constraint specification"""
+ line = lines[i].strip()
+
+ # Check for old format with comma
+ if ',' in line:
+ parts = line.split(',', 1)
+ constraint = {
+ 'name': parts[0].strip(),
+ 'desc': parts[1].strip() if len(parts) > 1 else '',
+ 'expr': None
+ }
+ else:
+ constraint = {'name': line, 'desc': '', 'expr': None}
+
+ subfields, next_i = self._parse_subfields(lines, i)
+
+ if 'desc' in subfields:
+ constraint['desc'] = (constraint['desc'] + ' ' + subfields['desc']).strip()
+ constraint['expr'] = subfields.get('expr')
+
+ return constraint, next_i
+
+ def _parse_side_effect_item(self, lines, i):
+ """Parse a single side effect specification"""
+ line = lines[i].strip()
+
+ # Default to new format
+ effect = {
+ 'type': line,
+ 'target': '',
+ 'desc': '',
+ 'condition': None,
+ 'reversible': False
+ }
+
+ # Check for old format with commas
+ if ',' in line:
+ # Handle condition and reversible flags
+ cond_match = re.search(r',\s*condition=([^,]+?)(?:\s*,\s*reversible=(yes|no)\s*)?$', line)
+ if cond_match:
+ effect['condition'] = cond_match.group(1).strip()
+ effect['reversible'] = cond_match.group(2) == 'yes'
+ line = line[:cond_match.start()]
+ elif ', reversible=yes' in line:
+ effect['reversible'] = True
+ line = line.replace(', reversible=yes', '')
+ elif ', reversible=no' in line:
+ line = line.replace(', reversible=no', '')
+
+ parts = line.split(',', 2)
+ if len(parts) >= 1:
+ effect['type'] = parts[0].strip()
+ if len(parts) >= 2:
+ effect['target'] = parts[1].strip()
+ if len(parts) >= 3:
+ effect['desc'] = parts[2].strip()
+ else:
+ # Multi-line format with subfields
+ subfields, next_i = self._parse_subfields(lines, i)
+ effect.update({
+ 'target': subfields.get('target', ''),
+ 'desc': subfields.get('desc', ''),
+ 'condition': subfields.get('condition'),
+ 'reversible': subfields.get('reversible', '').lower() == 'yes'
+ })
+ return effect, next_i
+
+ return effect, i + 1
+
+ def _parse_state_trans_item(self, lines, i):
+ """Parse a single state transition specification"""
+ line = lines[i].strip()
+
+ trans = {
+ 'target': line,
+ 'from': '',
+ 'to': '',
+ 'condition': '',
+ 'desc': ''
+ }
+
+ # Check for old format with commas
+ if ',' in line:
+ parts = line.split(',', 3)
+ if len(parts) >= 1:
+ trans['target'] = parts[0].strip()
+ if len(parts) >= 2:
+ trans['from'] = parts[1].strip()
+ if len(parts) >= 3:
+ trans['to'] = parts[2].strip()
+ if len(parts) >= 4:
+ desc_part = parts[3].strip()
+ desc_parts = desc_part.split(',', 1)
+ if len(desc_parts) > 1:
+ trans['condition'] = desc_parts[0].strip()
+ trans['desc'] = desc_parts[1].strip()
+ else:
+ trans['desc'] = desc_part
+ return trans, i + 1
+ else:
+ # Multi-line format with subfields
+ subfields, next_i = self._parse_subfields(lines, i)
+ trans.update({
+ 'from': subfields.get('from', ''),
+ 'to': subfields.get('to', ''),
+ 'condition': subfields.get('condition', ''),
+ 'desc': subfields.get('desc', '')
+ })
+ return trans, next_i
+
+ def _process_parameters(self, sections, parameterlist, parameterdescs, parametertypes):
+ """Process and output parameter specifications"""
+ param_count = len(parameterlist)
+ if param_count > 0:
+ self.data += f"\n\tKAPI_PARAM_COUNT({param_count})\n"
+
+ for param_idx, param in enumerate(parameterlist):
+ param_name = param.strip()
+ param_desc = parameterdescs.get(param_name, '')
+ param_ctype = parametertypes.get(param_name, '')
+
+ # Parse parameter specifications
+ param_section = self._get_raw_section(sections, 'param')
+ param_specs = {}
+ if param_section:
+ param_specs = self._parse_param_spec(param_section, param_name)
+
+ self.data += f"\n\tKAPI_PARAM({param_idx}, {self._format_macro_param(param_name)}, "
+ self.data += f"{self._format_macro_param(param_ctype)}, {self._format_macro_param(param_desc)})\n"
+
+ # Add parameter attributes
+ for key, macro in [
+ ('param-type', 'KAPI_PARAM_TYPE'),
+ ('param-flags', 'KAPI_PARAM_FLAGS'),
+ ('param-size', 'KAPI_PARAM_SIZE'),
+ ('param-alignment', 'KAPI_PARAM_ALIGNMENT'),
+ ]:
+ if key in param_specs:
+ self.data += f"\t\t{macro}({param_specs[key]})\n"
+
+ # Handle constraint type
+ if 'param-constraint-type' in param_specs:
+ ctype = param_specs['param-constraint-type']
+ if ctype == 'KAPI_CONSTRAINT_BITMASK':
+ ctype = 'KAPI_CONSTRAINT_MASK'
+ self.data += f"\t\tKAPI_PARAM_CONSTRAINT_TYPE({ctype})\n"
+
+ # Handle range
+ if 'param-range' in param_specs and ',' in param_specs['param-range']:
+ min_val, max_val = param_specs['param-range'].split(',', 1)
+ self.data += f"\t\tKAPI_PARAM_RANGE({min_val.strip()}, {max_val.strip()})\n"
+
+ # Handle mask
+ if 'param-mask' in param_specs:
+ self.data += f"\t\tKAPI_PARAM_VALID_MASK({param_specs['param-mask']})\n"
+
+ # Handle enum values
+ if 'param-enum-values' in param_specs:
+ self.data += f"\t\tKAPI_PARAM_ENUM_VALUES({param_specs['param-enum-values']})\n"
+
+ # Handle size parameter index
+ if 'param-size-param' in param_specs:
+ self.data += f"\t\tKAPI_PARAM_SIZE_PARAM({param_specs['param-size-param']})\n"
+
+ # Handle constraint description
+ if 'param-constraint' in param_specs:
+ self.data += f"\t\tKAPI_PARAM_CONSTRAINT({self._format_macro_param(param_specs['param-constraint'])})\n"
+
+ self.data += "\tKAPI_PARAM_END\n"
+
+ def _parse_param_spec(self, section_content, param_name):
+ """Parse parameter specifications from indented format"""
+ specs = {}
+ lines = section_content.strip().split('\n')
+ current_item = None
+
+ # Map to expected keys
+ field_map = {
+ 'type': 'param-type',
+ 'flags': 'param-flags',
+ 'size': 'param-size',
+ 'constraint-type': 'param-constraint-type',
+ 'constraint': 'param-constraint',
+ 'cdesc': 'param-constraint',
+ 'range': 'param-range',
+ 'mask': 'param-mask',
+ 'valid-mask': 'param-mask',
+ 'valid-values': 'param-enum-values',
+ 'alignment': 'param-alignment',
+ 'size-param': 'param-size-param',
+ 'struct-type': 'param-struct-type',
+ }
+
+ i = 0
+ while i < len(lines):
+ line = lines[i]
+ if not line.strip():
+ i += 1
+ continue
+
+ # Check if this is our parameter (non-indented line)
+ if not line.startswith((' ', '\t')):
+ parts = line.strip().split(',', 1)
+ current_item = param_name if parts[0].strip() == param_name else None
+ if current_item and len(parts) > 1:
+ specs['param-type'] = parts[1].strip()
+ i += 1
+ elif current_item == param_name:
+ # Parse subfield
+ stripped = line.strip()
+ if ':' in stripped:
+ key, value = stripped.split(':', 1)
+ key = key.strip()
+ value = value.strip()
+
+ # Collect continuation lines (indented lines without a colon that
+ # defines a new key, i.e., lines that are pure continuations)
+ i += 1
+ while i < len(lines):
+ next_line = lines[i]
+ # Stop if we hit a non-indented line (new param)
+ if next_line.strip() and not next_line.startswith((' ', '\t')):
+ break
+ next_stripped = next_line.strip()
+ # Stop if we hit a new key (contains colon with known key prefix)
+ if next_stripped and ':' in next_stripped:
+ potential_key = next_stripped.split(':', 1)[0].strip()
+ if potential_key in field_map or potential_key in ['type', 'desc']:
+ break
+ # This is a continuation line
+ if next_stripped:
+ value = value + ' ' + next_stripped
+ i += 1
+
+ if key in field_map:
+ # Clean up the value - remove excessive whitespace
+ value = ' '.join(value.split())
+ specs[field_map[key]] = value
+ else:
+ i += 1
+
+ return specs
+
+ def _validate_effect_type(self, effect_type):
+ """Validate and normalize effect type"""
+ if 'KAPI_EFFECT_SCHEDULER' in effect_type:
+ return effect_type.replace('KAPI_EFFECT_SCHEDULER', 'KAPI_EFFECT_SCHEDULE')
+
+ if 'KAPI_EFFECT_' in effect_type and effect_type not in VALID_EFFECT_TYPES:
+ if '|' in effect_type:
+ parts = [p.strip() for p in effect_type.split('|')]
+ valid_parts = []
+ for p in parts:
+ if p in VALID_EFFECT_TYPES:
+ valid_parts.append(p)
+ else:
+ import sys
+ print(f"warning: unrecognized effect type '{p}', "
+ f"defaulting to KAPI_EFFECT_MODIFY_STATE", file=sys.stderr)
+ valid_parts.append('KAPI_EFFECT_MODIFY_STATE')
+ return ' | '.join(valid_parts)
+ import sys
+ print(f"warning: unrecognized effect type '{effect_type}', "
+ f"defaulting to KAPI_EFFECT_MODIFY_STATE", file=sys.stderr)
+ return 'KAPI_EFFECT_MODIFY_STATE'
+
+ return effect_type
+
+ def _has_api_spec(self, sections):
+ """Check if this function has an API specification.
+
+ Returns True if at least 2 KAPI-specific section indicators are present.
+ We require 2+ indicators (not just 1) to avoid false positives from
+ regular kernel-doc comments that happen to use a common section name
+ like 'return' or 'error'. Having multiple KAPI sections strongly
+ suggests intentional API specification rather than coincidence.
+ """
+ indicators = [
+ 'api-type', 'context-flags', 'param-type', 'error-code',
+ 'capability', 'signal', 'lock', 'state-trans', 'constraint',
+ 'side-effect', 'long-desc'
+ ]
+
+ count = sum(1 for ind in indicators
+ if any(key.lower().startswith(ind.lower()) or
+ key.lower().startswith('@' + ind.lower())
+ for key in sections.keys()))
+
+ # Require 2+ indicators to distinguish from regular kernel-doc
+ return count >= 2
+
+ def out_function(self, fname, name, args):
+ """Generate API spec for a function"""
+ function_name = args.get('function', name)
+ sections = args.sections if hasattr(args, 'sections') else args.get('sections', {})
+
+ if not self._has_api_spec(sections):
+ return
+
+ parameterlist = args.parameterlist if hasattr(args, 'parameterlist') else args.get('parameterlist', [])
+ parameterdescs = args.parameterdescs if hasattr(args, 'parameterdescs') else args.get('parameterdescs', {})
+ parametertypes = args.parametertypes if hasattr(args, 'parametertypes') else args.get('parametertypes', {})
+ purpose = args.get('purpose', '')
+
+ # Start macro invocation
+ self.data += f"DEFINE_KERNEL_API_SPEC({function_name})\n"
+
+ # Basic info
+ if purpose:
+ self.data += f"\tKAPI_DESCRIPTION({self._format_macro_param(purpose)})\n"
+
+ long_desc = self._get_multiline_section(sections, 'long-desc')
+ if long_desc:
+ self.data += f"\tKAPI_LONG_DESC({self._format_macro_param(long_desc, KAPI_MAX_LONG_DESC_LEN)})\n"
+
+ # Context flags
+ context = self._get_section(sections, 'context-flags') or self._get_section(sections, 'context')
+ if context:
+ self.data += f"\tKAPI_CONTEXT({context})\n"
+
+ # Process parameters
+ self._process_parameters(sections, parameterlist, parameterdescs, parametertypes)
+
+ # Process return value
+ self._process_return(sections)
+
+ # Process errors
+ errors = self._parse_indented_items(
+ self._get_raw_section(sections, 'error'),
+ self._parse_error_item
+ )
+
+ if errors:
+ self.data += f"\n\tKAPI_ERROR_COUNT({len(errors)})\n"
+
+ for idx, error in enumerate(errors):
+ self._output_error(idx, error)
+
+ # Process signals
+ signals = self._parse_indented_items(
+ self._get_raw_section(sections, 'signal'),
+ self._parse_signal_item
+ )
+
+ if signals:
+ self.data += f"\n\tKAPI_SIGNAL_COUNT({len(signals)})\n"
+
+ for idx, signal in enumerate(signals):
+ self._output_signal(idx, signal)
+
+ # Process other specifications
+ self._process_locks(sections)
+ self._process_constraints(sections)
+ self._process_side_effects(sections)
+ self._process_state_transitions(sections)
+ self._process_capabilities(sections)
+
+ # Add examples and notes (use multiline extraction for full content)
+ for key, macro, max_len in [
+ ('examples', 'KAPI_EXAMPLES', KAPI_MAX_EXAMPLES_LEN),
+ ('notes', 'KAPI_NOTES', KAPI_MAX_NOTES_LEN),
+ ]:
+ value = self._get_multiline_section(sections, key)
+ if value:
+ self.data += f"\n\t{macro}({self._format_macro_param(value, max_len)})\n"
+
+ self.data += "\nKAPI_END_SPEC;\n\n"
+
+ def _process_return(self, sections):
+ """Process the return value specification from kerneldoc annotations"""
+ raw = self._get_raw_section(sections, 'return')
+ if not raw:
+ return
+
+ # Parse subfields from the return section, handling continuation lines
+ lines = raw.strip().split('\n')
+ subfields = {}
+ current_key = None
+ for line in lines:
+ stripped = line.strip()
+ if ':' in stripped and not stripped.startswith(' '):
+ key, value = stripped.split(':', 1)
+ current_key = key.strip()
+ subfields[current_key] = value.strip()
+ elif current_key and stripped:
+ # Continuation line
+ subfields[current_key] += ' ' + stripped
+
+ ret_type = subfields.get('type', '')
+ check_type = subfields.get('check-type', '')
+ desc = subfields.get('desc', '')
+ success = subfields.get('success', '')
+
+ if not ret_type and not desc:
+ return
+
+ self.data += f"\n\tKAPI_RETURN({self._format_macro_param(ret_type)}, "
+ self.data += f"{self._format_macro_param(desc)})\n"
+
+ if ret_type:
+ self.data += f"\t\tKAPI_RETURN_TYPE({ret_type})\n"
+
+ if check_type:
+ self.data += f"\t\tKAPI_RETURN_CHECK_TYPE({check_type})\n"
+
+ if success and check_type == 'KAPI_RETURN_RANGE':
+ self.data += f"\t\tKAPI_RETURN_SUCCESS_RANGE(0, S64_MAX)\n"
+
+ self.data += "\tKAPI_RETURN_END\n"
+
+ def _output_error(self, idx, error):
+ """Output a single error specification"""
+ line = error['line']
+ if line.startswith('-'):
+ line = line[1:].strip()
+
+ parts = line.split(',', 2)
+ if len(parts) == 2:
+ # Format: NAME, description
+ name = parts[0].strip()
+ short_desc = parts[1].strip()
+ code = f"-{name}"
+ elif len(parts) >= 3:
+ # Format: code, name, description
+ code = parts[0].strip()
+ name = parts[1].strip()
+ short_desc = parts[2].strip()
+ if not code.startswith('-'):
+ code = f"-{code}"
+ else:
+ return
+
+ long_desc = error.get('desc', '') or short_desc
+
+ self.data += f"\n\tKAPI_ERROR({idx}, {code}, {self._format_macro_param(name)}, "
+ self.data += f"{self._format_macro_param(short_desc)},\n\t\t {self._format_macro_param(long_desc)})\n"
+
+ def _output_signal(self, idx, signal):
+ """Output a single signal specification"""
+ self.data += f"\n\tKAPI_SIGNAL({idx}, {signal['number']}, "
+ self.data += f"{self._format_macro_param(signal['name'], KAPI_MAX_SIGNAL_NAME_LEN)}, "
+ self.data += f"{signal['direction']}, {signal['action']})\n"
+
+ for key, macro in [
+ ('condition', 'KAPI_SIGNAL_CONDITION'),
+ ('desc', 'KAPI_SIGNAL_DESC'),
+ ('error', 'KAPI_SIGNAL_ERROR'),
+ ('timing', 'KAPI_SIGNAL_TIMING'),
+ ('priority', 'KAPI_SIGNAL_PRIORITY'),
+ ]:
+ if signal.get(key):
+ # Priority field is numeric
+ if key == 'priority':
+ self.data += f"\t\t{macro}({signal[key]})\n"
+ else:
+ self.data += f"\t\t{macro}({self._format_macro_param(signal[key])})\n"
+
+ if signal.get('interruptible'):
+ self.data += "\t\tKAPI_SIGNAL_INTERRUPTIBLE\n"
+
+ self.data += "\tKAPI_SIGNAL_END\n"
+
+ def _process_locks(self, sections):
+ """Process lock specifications"""
+ locks = self._parse_indented_items(
+ self._get_raw_section(sections, 'lock'),
+ self._parse_lock_item
+ )
+
+ if locks:
+ self.data += f"\n\tKAPI_LOCK_COUNT({len(locks)})\n"
+
+ for idx, lock in enumerate(locks):
+ self.data += f"\n\tKAPI_LOCK({idx}, {self._format_macro_param(lock['name'])}, {lock['type']})\n"
+
+ for flag in ['acquired', 'released']:
+ if lock.get(flag):
+ self.data += f"\t\tKAPI_LOCK_{flag.upper()}\n"
+
+ if lock.get('desc'):
+ self.data += f"\t\tKAPI_LOCK_DESC({self._format_macro_param(lock['desc'])})\n"
+
+ self.data += "\tKAPI_LOCK_END\n"
+
+ def _process_constraints(self, sections):
+ """Process constraint specifications"""
+ constraints = self._parse_indented_items(
+ self._get_raw_section(sections, 'constraint'),
+ self._parse_constraint_item
+ )
+
+ if constraints:
+ self.data += f"\n\tKAPI_CONSTRAINT_COUNT({len(constraints)})\n"
+
+ for idx, constraint in enumerate(constraints):
+ self.data += f"\n\tKAPI_CONSTRAINT({idx}, {self._format_macro_param(constraint['name'])},\n"
+ self.data += f"\t\t\t{self._format_macro_param(constraint['desc'])})\n"
+
+ if constraint.get('expr'):
+ self.data += f"\t\tKAPI_CONSTRAINT_EXPR({self._format_macro_param(constraint['expr'])})\n"
+
+ self.data += "\tKAPI_CONSTRAINT_END\n"
+
+ def _process_side_effects(self, sections):
+ """Process side effect specifications"""
+ effects = self._parse_indented_items(
+ self._get_raw_section(sections, 'side-effect'),
+ self._parse_side_effect_item
+ )
+
+ if effects:
+ self.data += f"\n\tKAPI_SIDE_EFFECT_COUNT({len(effects)})\n"
+
+ for idx, effect in enumerate(effects):
+ effect_type = self._validate_effect_type(effect['type'])
+
+ self.data += f"\n\tKAPI_SIDE_EFFECT({idx}, {effect_type},\n"
+ self.data += f"\t\t\t {self._format_macro_param(effect['target'])},\n"
+ self.data += f"\t\t\t {self._format_macro_param(effect['desc'])})\n"
+
+ if effect.get('condition'):
+ self.data += f"\t\tKAPI_EFFECT_CONDITION({self._format_macro_param(effect['condition'])})\n"
+
+ if effect.get('reversible'):
+ self.data += "\t\tKAPI_EFFECT_REVERSIBLE\n"
+
+ self.data += "\tKAPI_SIDE_EFFECT_END\n"
+
+ def _process_state_transitions(self, sections):
+ """Process state transition specifications"""
+ transitions = self._parse_indented_items(
+ self._get_raw_section(sections, 'state-trans'),
+ self._parse_state_trans_item
+ )
+
+ if transitions:
+ self.data += f"\n\tKAPI_STATE_TRANS_COUNT({len(transitions)})\n"
+
+ for idx, trans in enumerate(transitions):
+ desc = trans['desc']
+ if trans.get('condition'):
+ desc = trans['condition'] + (', ' + desc if desc else '')
+
+ self.data += f"\n\tKAPI_STATE_TRANS({idx}, {self._format_macro_param(trans['target'])}, "
+ self.data += f"{self._format_macro_param(trans['from'])}, {self._format_macro_param(trans['to'])},\n"
+ self.data += f"\t\t\t {self._format_macro_param(desc)})\n"
+ self.data += "\tKAPI_STATE_TRANS_END\n"
+
+ def _process_capabilities(self, sections):
+ """Process capability specifications"""
+ cap_section = self._get_raw_section(sections, 'capability')
+ if not cap_section:
+ return
+
+ lines = cap_section.strip().split('\n')
+ capabilities = []
+ i = 0
+
+ while i < len(lines):
+ line = lines[i].strip()
+ # Skip empty lines and subfield lines (they'll be parsed with their parent)
+ if not line or line.startswith(('allows:', 'without:', 'condition:', 'priority:', 'type:', 'desc:')):
+ i += 1
+ continue
+
+ cap_info = {'line': line}
+
+ # Parse subfields
+ subfields, next_i = self._parse_subfields(lines, i)
+ cap_info.update(subfields)
+ capabilities.append(cap_info)
+ i = next_i
+
+ if capabilities:
+ # Filter out "none" capabilities (no capability required)
+ valid_caps = [cap for cap in capabilities if cap['line'].strip().lower() != 'none']
+
+ if not valid_caps:
+ return
+
+ self.data += f"\n\tKAPI_CAPABILITY_COUNT({len(valid_caps)})\n"
+
+ for idx, cap in enumerate(valid_caps):
+ line = cap['line']
+ parts = line.split(',', 2)
+
+ # Handle both formats:
+ # 1. New format: "CAP_NAME" with type/desc as subfields
+ # 2. Old format: "CAP_NAME, TYPE, description"
+ if len(parts) >= 2:
+ # Old comma-separated format
+ cap_name = parts[0].strip()
+ cap_type = parts[1].strip()
+ cap_desc = parts[2].strip() if len(parts) > 2 else cap.get('desc', cap_name)
+ else:
+ # New subfield format - capability name on main line
+ cap_name = line.strip()
+ cap_type = cap.get('type', 'KAPI_CAP_PERFORM_OPERATION')
+ cap_desc = cap.get('desc', cap_name)
+
+ # Map capability type to valid kernel enum values
+ cap_type_map = {
+ 'KAPI_CAP_REQUIRED': 'KAPI_CAP_PERFORM_OPERATION',
+ 'required': 'KAPI_CAP_PERFORM_OPERATION',
+ 'bypass': 'KAPI_CAP_BYPASS_CHECK',
+ 'grant': 'KAPI_CAP_GRANT_PERMISSION',
+ 'override': 'KAPI_CAP_OVERRIDE_RESTRICTION',
+ 'access': 'KAPI_CAP_ACCESS_RESOURCE',
+ 'modify': 'KAPI_CAP_MODIFY_BEHAVIOR',
+ 'limit': 'KAPI_CAP_INCREASE_LIMIT',
+ }
+ cap_type = cap_type_map.get(cap_type, cap_type)
+
+ # Fix common type issues
+ if 'BYPASS' in cap_type and cap_type != 'KAPI_CAP_BYPASS_CHECK':
+ cap_type = 'KAPI_CAP_BYPASS_CHECK'
+
+ # Ensure cap_type is a valid enum
+ valid_types = [
+ 'KAPI_CAP_BYPASS_CHECK', 'KAPI_CAP_INCREASE_LIMIT',
+ 'KAPI_CAP_OVERRIDE_RESTRICTION', 'KAPI_CAP_GRANT_PERMISSION',
+ 'KAPI_CAP_MODIFY_BEHAVIOR', 'KAPI_CAP_ACCESS_RESOURCE',
+ 'KAPI_CAP_PERFORM_OPERATION'
+ ]
+ if cap_type not in valid_types:
+ cap_type = 'KAPI_CAP_PERFORM_OPERATION'
+
+ self.data += f"\n\tKAPI_CAPABILITY({idx}, {cap_name}, {self._format_macro_param(cap_desc)}, {cap_type})\n"
+
+ for key, macro in [
+ ('allows', 'KAPI_CAP_ALLOWS'),
+ ('without', 'KAPI_CAP_WITHOUT'),
+ ('condition', 'KAPI_CAP_CONDITION'),
+ ('priority', 'KAPI_CAP_PRIORITY'),
+ ]:
+ if cap.get(key):
+ value = self._format_macro_param(cap[key]) if key != 'priority' else cap[key]
+ self.data += f"\t\t{macro}({value})\n"
+
+ self.data += "\tKAPI_CAPABILITY_END\n"
+
+ # Skip output methods for non-function types
+ def out_enum(self, fname, name, args): pass
+ def out_typedef(self, fname, name, args): pass
+ def out_struct(self, fname, name, args): pass
+ def out_doc(self, fname, name, args): pass
diff --git a/tools/lib/python/kdoc/kdoc_output.py b/tools/lib/python/kdoc/kdoc_output.py
index 4210b91dde5f1..cd91a4f59f275 100644
--- a/tools/lib/python/kdoc/kdoc_output.py
+++ b/tools/lib/python/kdoc/kdoc_output.py
@@ -129,8 +129,13 @@ class OutputFormat:
Output warnings for identifiers that will be displayed.
"""
- for log_msg in args.warnings:
- self.config.warning(log_msg)
+ warnings = getattr(args, 'warnings', [])
+
+ for log_msg in warnings:
+ # Skip numeric warnings (line numbers) which are false positives
+ # from parameter-specific sections like "param-constraint: name, value"
+ if not isinstance(log_msg, int):
+ self.config.warning(log_msg)
def check_doc(self, name, args):
"""Check if DOC should be output."""
diff --git a/tools/lib/python/kdoc/kdoc_parser.py b/tools/lib/python/kdoc/kdoc_parser.py
index ca00695b47b31..2b90c2242e57c 100644
--- a/tools/lib/python/kdoc/kdoc_parser.py
+++ b/tools/lib/python/kdoc/kdoc_parser.py
@@ -28,6 +28,23 @@ from kdoc.kdoc_item import KdocItem
# Allow whitespace at end of comment start.
doc_start = KernRe(r'^/\*\*\s*$', cache=False)
+# Sections that are allowed to be duplicated for API specifications
+# These represent lists of items (multiple errors, signals, etc.)
+ALLOWED_DUPLICATE_SECTIONS = {
+ 'param', '@param',
+ 'error', '@error',
+ 'signal', '@signal',
+ 'lock', '@lock',
+ 'side-effect', '@side-effect',
+ 'state-trans', '@state-trans',
+ 'capability', '@capability',
+ 'constraint', '@constraint',
+ 'validation-group', '@validation-group',
+ 'validation-rule', '@validation-rule',
+ 'validation-flag', '@validation-flag',
+ 'struct-field', '@struct-field',
+}
+
doc_end = KernRe(r'\*/', cache=False)
doc_com = KernRe(r'\s*\*\s*', cache=False)
doc_com_body = KernRe(r'\s*\* ?', cache=False)
@@ -40,10 +57,70 @@ doc_decl = doc_com + KernRe(r'(\w+)', cache=False)
# @{section-name}:
# while trying to not match literal block starts like "example::"
#
+# Base kernel-doc section names
known_section_names = 'description|context|returns?|notes?|examples?'
-known_sections = KernRe(known_section_names, flags = re.I)
+
+# API specification section names (for KAPI spec framework)
+# Format: (base_name, has_count_variant, has_other_variants)
+# Sections with has_count_variant=True need negative lookahead in doc_sect
+# to avoid matching 'error' when 'error-count' is intended
+_kapi_base_sections = [
+ # (name, needs_lookahead, additional_variants)
+ ('api-type', False, []),
+ ('api-version', False, []),
+ ('param', True, []), # has param-count
+ ('struct', True, ['struct-type', 'struct-field', 'struct-field-[a-z\\-]+']),
+ ('validation-group', False, []),
+ ('validation-policy', False, []),
+ ('validation-flag', False, []),
+ ('validation-rule', False, []),
+ ('error', True, ['error-code', 'error-condition']),
+ ('capability', True, []),
+ ('signal', True, []),
+ ('lock', True, []),
+ ('context-flags', False, []),
+ ('return', True, ['return-type', 'return-check', 'return-check-type',
+ 'return-success', 'return-desc']),
+ ('long-desc', False, []),
+ ('constraint', True, []),
+ ('side-effect', True, []),
+ ('state-trans', True, []),
+]
+
+def _build_kapi_patterns():
+ """Build KAPI section patterns from the base definitions."""
+ validation_parts = [] # For known_sections (simple validation)
+ parsing_parts = [] # For doc_sect (with negative lookaheads)
+
+ for name, has_count, variants in _kapi_base_sections:
+ # Add base name (with optional @ prefix)
+ validation_parts.append(f'@?{name}')
+ if has_count:
+ # Need negative lookahead to not match 'name-count' or 'name-*'
+ parsing_parts.append(f'@?{name}(?!-)')
+ validation_parts.append(f'@?{name}-count')
+ parsing_parts.append(f'@?{name}-count')
+ else:
+ parsing_parts.append(f'@?{name}')
+
+ # Add variants
+ for variant in variants:
+ validation_parts.append(f'@?{variant}')
+ parsing_parts.append(f'@?{variant}')
+
+ # Add catch-all for kapi-* extensions
+ validation_parts.append(r'@?kapi-.*')
+ parsing_parts.append(r'@?kapi-.*')
+
+ return '|'.join(validation_parts), '|'.join(parsing_parts)
+
+_kapi_validation_pattern, _kapi_parsing_pattern = _build_kapi_patterns()
+
+known_sections = KernRe(known_section_names + '|' + _kapi_validation_pattern,
+ flags=re.I)
doc_sect = doc_com + \
- KernRe(r'\s*(@[.\w]+|@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$',
+ KernRe(r'\s*(@[.\w\-]+|@\.\.\.|' + known_section_names + '|' +
+ _kapi_parsing_pattern + r')\s*:([^:].*)?$',
flags=re.I, cache=False)
doc_content = doc_com_body + KernRe(r'(.*)', cache=False)
@@ -349,7 +426,9 @@ class KernelEntry:
else:
if name in self.sections and self.sections[name] != "":
# Only warn on user-specified duplicate section names
- if name != SECTION_DEFAULT:
+ # Skip warning for sections that are expected to have duplicates
+ # (like error, param, signal, etc. for API specifications)
+ if name != SECTION_DEFAULT and name not in ALLOWED_DUPLICATE_SECTIONS:
self.emit_msg(self.new_start_line,
f"duplicate section name '{name}'")
# Treat as a new paragraph - add a blank line
--
2.51.0
^ permalink raw reply related
* [PATCH v2 1/9] kernel/api: introduce kernel API specification framework
From: Sasha Levin @ 2026-03-22 12:10 UTC (permalink / raw)
To: linux-api, linux-kernel
Cc: linux-doc, linux-fsdevel, linux-kbuild, linux-kselftest,
workflows, tools, x86, Thomas Gleixner, Paul E . McKenney,
Greg Kroah-Hartman, Jonathan Corbet, Dmitry Vyukov, Randy Dunlap,
Cyril Hrubis, Kees Cook, Jake Edge, David Laight, Askar Safin,
Gabriele Paoloni, Mauro Carvalho Chehab, Christian Brauner,
Alexander Viro, Andrew Morton, Masahiro Yamada, Shuah Khan,
Ingo Molnar, Arnd Bergmann, Sasha Levin
In-Reply-To: <20260322121026.869758-1-sashal@kernel.org>
Add a framework for formally documenting kernel APIs with inline
specifications. This framework provides:
- Structured API documentation with parameter specifications, return
values, error conditions, and execution context requirements
- Runtime validation capabilities for debugging (CONFIG_KAPI_RUNTIME_CHECKS)
- Support for both internal kernel APIs and system calls
The framework stores specifications in a dedicated ELF section and
provides infrastructure for:
- Compile-time validation of specifications
- Runtime querying of API documentation
- Integration with existing SYSCALL_DEFINE macros
This commit introduces the core infrastructure without modifying any
existing APIs. Subsequent patches will add specifications to individual
subsystems.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
.gitignore | 1 +
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kernel-api-spec.rst | 576 +++++++
MAINTAINERS | 11 +
arch/x86/include/asm/syscall_wrapper.h | 40 +
include/asm-generic/vmlinux.lds.h | 28 +
include/linux/kernel_api_spec.h | 1580 +++++++++++++++++++
include/linux/syscall_api_spec.h | 186 +++
include/linux/syscalls.h | 39 +
init/Kconfig | 2 +
kernel/Makefile | 3 +
kernel/api/.gitignore | 2 +
kernel/api/Kconfig | 70 +
kernel/api/Makefile | 14 +
kernel/api/kapi_kunit.c | 538 +++++++
kernel/api/kernel_api_spec.c | 1277 +++++++++++++++
scripts/generate_api_specs.sh | 18 +
17 files changed, 4386 insertions(+)
create mode 100644 Documentation/dev-tools/kernel-api-spec.rst
create mode 100644 include/linux/kernel_api_spec.h
create mode 100644 include/linux/syscall_api_spec.h
create mode 100644 kernel/api/.gitignore
create mode 100644 kernel/api/Kconfig
create mode 100644 kernel/api/Makefile
create mode 100644 kernel/api/kapi_kunit.c
create mode 100644 kernel/api/kernel_api_spec.c
create mode 100755 scripts/generate_api_specs.sh
diff --git a/.gitignore b/.gitignore
index 3a7241c941f5e..7130001e444f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
#
.*
*.a
+*.apispec.h
*.asn1.[ch]
*.bin
*.bz2
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index 59cbb77b33ff4..8d3768645d96c 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -36,6 +36,7 @@ Documentation/process/debugging/index.rst
kunit/index
ktap
checkuapi
+ kernel-api-spec
gpio-sloppy-logic-analyzer
autofdo
propeller
diff --git a/Documentation/dev-tools/kernel-api-spec.rst b/Documentation/dev-tools/kernel-api-spec.rst
new file mode 100644
index 0000000000000..505bcb76e805c
--- /dev/null
+++ b/Documentation/dev-tools/kernel-api-spec.rst
@@ -0,0 +1,576 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Kernel API Specification Framework
+======================================
+
+:Author: Sasha Levin <sashal@kernel.org>
+
+.. contents:: Table of Contents
+ :depth: 3
+ :local:
+
+Introduction
+============
+
+The Kernel API Specification Framework (KAPI) provides a comprehensive system for
+formally documenting, validating, and introspecting kernel APIs. This framework
+addresses the long-standing challenge of maintaining accurate, machine-readable
+documentation for the thousands of internal kernel APIs and system calls.
+
+Purpose and Goals
+-----------------
+
+The framework aims to:
+
+1. **Improve API Documentation**: Provide structured, inline documentation that
+ lives alongside the code and is maintained as part of the development process.
+
+2. **Enable Runtime Validation**: Optionally validate API usage at runtime to catch
+ common programming errors during development and testing.
+
+3. **Support Tooling**: Export API specifications in machine-readable formats for
+ use by static analyzers, documentation generators, and development tools.
+
+4. **Formalize Contracts**: Explicitly document API contracts including parameter
+ constraints, execution contexts, locking requirements, and side effects.
+
+Architecture Overview
+=====================
+
+Components
+----------
+
+The framework consists of several key components:
+
+1. **Core Framework** (``kernel/api/kernel_api_spec.c``)
+
+ - API specification registration and storage
+ - Runtime validation engine
+ - Specification lookup and querying
+
+2. **DebugFS Interface** (``kernel/api/kapi_debugfs.c``)
+
+ - Runtime introspection via ``/sys/kernel/debug/kapi/``
+ - Per-API detailed specification output
+ - List of all registered API specifications
+
+3. **kapi Tool** (``tools/kapi/``)
+
+ - Userspace utility for extracting specifications
+ - Multiple input sources (source, binary, debugfs)
+ - Multiple output formats (plain, JSON, RST)
+ - Testing and validation utilities
+
+Data Model
+----------
+
+The framework uses a hierarchical data model::
+
+ kernel_api_spec
+ ├── Basic Information
+ │ ├── name (API function name)
+ │ ├── version (specification version)
+ │ └── description (human-readable description)
+ │
+ ├── Parameters (up to 16)
+ │ └── kapi_param_spec
+ │ ├── name
+ │ ├── type (int, pointer, string, etc.)
+ │ ├── direction (in, out, inout)
+ │ ├── constraints (range, mask, enum values)
+ │ └── validation rules
+ │
+ ├── Return Value
+ │ └── kapi_return_spec
+ │ ├── type
+ │ ├── success conditions
+ │ └── validation rules
+ │
+ ├── Error Conditions (up to 32)
+ │ └── kapi_error_spec
+ │ ├── error code
+ │ ├── condition description
+ │ └── recovery advice
+ │
+ ├── Execution Context
+ │ ├── allowed contexts (process, interrupt, etc.)
+ │ ├── locking requirements
+ │ └── preemption/interrupt state
+ │
+ └── Side Effects
+ ├── memory allocation
+ ├── state changes
+ └── signal handling
+
+Usage Guide
+===========
+
+Basic API Specification
+-----------------------
+
+API specifications are written as KAPI-annotated kerneldoc comments directly in
+the source file, immediately preceding the function implementation. The ``kapi``
+tool extracts these annotations to produce structured specifications.
+
+.. code-block:: c
+
+ /**
+ * kmalloc - allocate kernel memory
+ * @size: Number of bytes to allocate
+ * @flags: Allocation flags (GFP_*)
+ *
+ * context-flags: KAPI_CTX_PROCESS | KAPI_CTX_SOFTIRQ | KAPI_CTX_HARDIRQ
+ * param-count: 2
+ *
+ * param: size
+ * type: KAPI_TYPE_UINT
+ * flags: KAPI_PARAM_IN
+ * constraint-type: KAPI_CONSTRAINT_RANGE
+ * range: 0, KMALLOC_MAX_SIZE
+ *
+ * param: flags
+ * type: KAPI_TYPE_UINT
+ * flags: KAPI_PARAM_IN
+ * constraint-type: KAPI_CONSTRAINT_MASK
+ * valid-mask: 0x1ffffff
+ *
+ * error: ENOMEM, Out of memory
+ * desc: Insufficient memory available for the requested allocation.
+ *
+ * side-effect: KAPI_EFFECT_ALLOC_MEMORY
+ * target: kernel heap
+ * desc: Allocates memory from kernel heap
+ */
+ void *kmalloc(size_t size, gfp_t flags)
+ {
+ /* Implementation */
+ }
+
+System Call Specification
+-------------------------
+
+System calls are documented inline in the implementation file (e.g., ``fs/open.c``)
+using KAPI-annotated kerneldoc comments. When ``CONFIG_KAPI_RUNTIME_CHECKS`` is
+enabled, the ``SYSCALL_DEFINEx`` macros automatically look up the specification
+and validate parameters before and after the syscall executes.
+
+Runtime Validation
+==================
+
+Enabling Validation
+-------------------
+
+Runtime validation is controlled by kernel configuration:
+
+1. Enable ``CONFIG_KAPI_SPEC`` to build the framework
+2. Enable ``CONFIG_KAPI_RUNTIME_CHECKS`` for runtime validation
+
+Validation Behavior
+-------------------
+
+When ``CONFIG_KAPI_RUNTIME_CHECKS`` is enabled, all registered API specifications
+are validated automatically at call time. The framework checks parameter constraints,
+execution context, and return values. Parameter violations are reported via
+``pr_warn_ratelimited`` and return value violations via ``WARN_ONCE`` to avoid
+flooding the kernel log.
+
+Custom Validators
+-----------------
+
+Parameters can use the ``KAPI_CONSTRAINT_CUSTOM`` constraint type to register
+custom validation functions via the ``validate`` field in the constraint spec:
+
+.. code-block:: c
+
+ static bool validate_buffer_size(s64 value)
+ {
+ size_t size = (size_t)value;
+
+ return size > 0 && size <= MAX_BUFFER_SIZE;
+ }
+
+ /* In the constraint definition: */
+ .type = KAPI_CONSTRAINT_CUSTOM,
+ .validate = validate_buffer_size,
+
+Performance Considerations
+==========================
+
+Memory Overhead
+---------------
+
+Each API specification consumes approximately 400-450KB of memory due to the
+fixed-size arrays in ``struct kernel_api_spec``. With the current 4 syscall
+specifications, total memory usage is approximately 1.7MB. Consider:
+
+1. Building with ``CONFIG_KAPI_SPEC=n`` for production kernels
+2. Using ``__init`` annotations for APIs only used during boot
+3. Implementing lazy loading for rarely used specifications
+
+Runtime Overhead
+----------------
+
+When ``CONFIG_KAPI_RUNTIME_CHECKS`` is enabled:
+
+- Each validated API call adds 50-200ns overhead
+- Complex validations (custom validators) may add more
+- Use validation only in development/testing kernels
+
+Optimization Strategies
+-----------------------
+
+1. **Compile-time optimization**: When validation is disabled, all
+ validation code is optimized away by the compiler.
+
+2. **Selective enablement**: Enable ``CONFIG_KAPI_RUNTIME_CHECKS``
+ only in development/testing kernels, not in production.
+
+Documentation Generation
+------------------------
+
+The framework exports specifications via debugfs that can be used
+to generate documentation. The ``kapi`` tool provides comprehensive
+extraction and formatting capabilities for kernel API specifications.
+
+The kapi Tool
+=============
+
+Overview
+--------
+
+The ``kapi`` tool is a userspace utility that extracts and displays kernel API
+specifications from multiple sources. It provides a unified interface to access
+API documentation whether from compiled kernels, source code, or runtime systems.
+
+Installation
+------------
+
+Build the tool from the kernel source tree::
+
+ $ cd tools/kapi
+ $ cargo build --release
+
+ # Optional: Install system-wide
+ $ cargo install --path .
+
+The tool requires Rust and Cargo to build. The binary will be available at
+``tools/kapi/target/release/kapi``.
+
+Command-Line Usage
+------------------
+
+Basic syntax::
+
+ kapi [OPTIONS] [API_NAME]
+
+Options:
+
+- ``--vmlinux <PATH>``: Extract from compiled kernel binary
+- ``--source <PATH>``: Extract from kernel source code
+- ``--debugfs <PATH>``: Extract from debugfs (default: /sys/kernel/debug)
+- ``-f, --format <FORMAT>``: Output format (plain, json, rst)
+- ``-h, --help``: Display help information
+- ``-V, --version``: Display version information
+
+Input Modes
+-----------
+
+**1. Source Code Mode**
+
+Extract specifications directly from kernel source::
+
+ # Scan entire kernel source tree
+ $ kapi --source /path/to/linux
+
+ # Extract from specific file
+ $ kapi --source kernel/sched/core.c
+
+ # Get details for specific API
+ $ kapi --source /path/to/linux sys_sched_yield
+
+**2. Vmlinux Mode**
+
+Extract from compiled kernel with debug symbols::
+
+ # List all APIs in vmlinux
+ $ kapi --vmlinux /boot/vmlinux-5.15.0
+
+ # Get specific syscall details
+ $ kapi --vmlinux ./vmlinux sys_read
+
+**3. Debugfs Mode**
+
+Extract from running kernel via debugfs::
+
+ # Use default debugfs path
+ $ kapi
+
+ # Use custom debugfs mount
+ $ kapi --debugfs /mnt/debugfs
+
+ # Get specific API from running kernel
+ $ kapi sys_write
+
+Output Formats
+--------------
+
+**Plain Text Format** (default)::
+
+ $ kapi sys_read
+
+ Detailed information for sys_read:
+ ==================================
+ Description: Read from a file descriptor
+
+ Detailed Description:
+ Reads up to count bytes from file descriptor fd into the buffer starting at buf.
+
+ Execution Context:
+ - KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE
+
+ Parameters (3):
+
+**JSON Format**::
+
+ $ kapi --format json sys_read
+ {
+ "api_details": {
+ "name": "sys_read",
+ "description": "Read from a file descriptor",
+ "long_description": "Reads up to count bytes...",
+ "context_flags": ["KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE"]
+ }
+ }
+
+**ReStructuredText Format**::
+
+ $ kapi --format rst sys_read
+
+ sys_read
+ ========
+
+ **Read from a file descriptor**
+
+ Reads up to count bytes from file descriptor fd into the buffer...
+
+Usage Examples
+--------------
+
+**Generate complete API documentation**::
+
+ # Export all kernel APIs to JSON
+ $ kapi --source /path/to/linux --format json > kernel-apis.json
+
+ # Generate RST documentation for all syscalls
+ $ kapi --vmlinux ./vmlinux --format rst > syscalls.rst
+
+ # List APIs from specific subsystem
+ $ kapi --source drivers/gpu/drm/
+
+**Integration with other tools**::
+
+ # Find all APIs that can sleep
+ $ kapi --format json | jq '.apis[] | select(.context_flags[] | contains("SLEEPABLE"))'
+
+ # Generate markdown documentation
+ $ kapi --format rst sys_mmap | pandoc -f rst -t markdown
+
+**Debugging and analysis**::
+
+ # Compare API between kernel versions
+ $ diff <(kapi --vmlinux vmlinux-5.10) <(kapi --vmlinux vmlinux-5.15)
+
+ # Check if specific API exists
+ $ kapi --source . my_custom_api || echo "API not found"
+
+Implementation Details
+----------------------
+
+The tool extracts API specifications from three sources:
+
+1. **Source Code**: Parses KAPI specification macros using regular expressions
+2. **Vmlinux**: Reads the ``.kapi_specs`` ELF section from compiled kernels
+3. **Debugfs**: Reads from ``/sys/kernel/debug/kapi/`` filesystem interface
+
+The tool supports all KAPI specification types:
+
+- System calls (kerneldoc annotations)
+- Kernel functions (kerneldoc annotations with KAPI tags)
+
+IDE Integration
+---------------
+
+Modern IDEs can use the specification data for:
+
+- Parameter hints
+- Type checking
+- Context validation
+- Error code documentation
+
+Testing Framework
+-----------------
+
+The framework includes test helpers::
+
+ #ifdef CONFIG_KAPI_TESTING
+ /* Verify API behaves according to specification */
+ kapi_test_api("kmalloc", test_cases);
+ #endif
+
+Best Practices
+==============
+
+Writing Specifications
+----------------------
+
+1. **Be Comprehensive**: Document all parameters, errors, and side effects
+2. **Keep Updated**: Update specs when API behavior changes
+3. **Use Examples**: Include usage examples in descriptions
+4. **Validate Constraints**: Define realistic constraints for parameters
+5. **Document Context**: Clearly specify allowed execution contexts
+
+Maintenance
+-----------
+
+1. **Version Specifications**: Increment version when API changes
+2. **Deprecation**: Mark deprecated APIs and suggest replacements
+3. **Cross-reference**: Link related APIs in descriptions
+4. **Test Specifications**: Verify specs match implementation
+
+Common Patterns
+---------------
+
+**Optional Parameters**:
+
+.. code-block:: c
+
+ /**
+ * @optional_arg: Optional argument (may be NULL)
+ *
+ * param: optional_arg
+ * type: KAPI_TYPE_PTR
+ * flags: KAPI_PARAM_IN | KAPI_PARAM_OPTIONAL
+ */
+
+**Buffer with Size Parameter**:
+
+.. code-block:: c
+
+ /**
+ * @buf: User-space buffer
+ *
+ * param: buf
+ * type: KAPI_TYPE_USER_PTR
+ * flags: KAPI_PARAM_OUT | KAPI_PARAM_USER
+ * constraint-type: KAPI_CONSTRAINT_BUFFER
+ * size-param: 2
+ */
+
+**Callback Functions**:
+
+.. code-block:: c
+
+ /**
+ * @callback: Callback function
+ *
+ * param: callback
+ * type: KAPI_TYPE_FUNC_PTR
+ * flags: KAPI_PARAM_IN
+ */
+
+Troubleshooting
+===============
+
+Common Issues
+-------------
+
+**Specification Not Found**::
+
+ kernel: KAPI: Specification for 'my_api' not found
+
+ Solution: Ensure the KAPI-annotated kerneldoc comment is in the
+ same translation unit as the function implementation.
+
+**Validation Failures**::
+
+ kernel: KAPI: Validation failed for kmalloc parameter 'size':
+ value 5242880 exceeds maximum 4194304
+
+ Solution: Check parameter constraints or adjust specification if
+ the constraint is incorrect.
+
+**Build Errors**::
+
+ error: 'KAPI_TYPE_UNKNOWN' undeclared
+
+ Solution: Include <linux/kernel_api_spec.h> and ensure
+ CONFIG_KAPI_SPEC is enabled.
+
+Debug Options
+-------------
+
+Enable verbose kernel logging to see KAPI validation messages::
+
+ echo 8 > /proc/sys/kernel/printk
+
+Future Directions
+=================
+
+Planned Features
+----------------
+
+1. **Automatic Extraction**: Tool to extract specifications from existing
+ kernel-doc comments
+
+2. **Contract Verification**: Static analysis to verify implementation
+ matches specification
+
+3. **Performance Profiling**: Measure actual API performance against
+ documented expectations
+
+4. **Fuzzing Integration**: Use specifications to guide intelligent
+ fuzzing of kernel APIs
+
+5. **Version Compatibility**: Track API changes across kernel versions
+
+Research Areas
+--------------
+
+1. **Formal Verification**: Use specifications for mathematical proofs
+ of correctness
+
+2. **Runtime Monitoring**: Detect specification violations in production
+ with minimal overhead
+
+3. **API Evolution**: Analyze how kernel APIs change over time
+
+4. **Security Applications**: Use specifications for security policy
+ enforcement
+
+Contributing
+============
+
+Submitting Specifications
+-------------------------
+
+1. Add specifications to the same file as the API implementation
+2. Follow existing patterns and naming conventions
+3. Test with CONFIG_KAPI_RUNTIME_CHECKS enabled
+4. Run scripts/checkpatch.pl on your changes
+
+Review Criteria
+---------------
+
+Specifications will be reviewed for:
+
+1. **Completeness**: All parameters and errors documented
+2. **Accuracy**: Specification matches implementation
+3. **Clarity**: Descriptions are clear and helpful
+4. **Consistency**: Follows framework conventions
+5. **Performance**: No unnecessary runtime overhead
+
+Contact
+-------
+
+- Maintainer: Sasha Levin <sashal@kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 96ea84948d76a..db2ba9a596eef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13804,6 +13804,17 @@ W: https://linuxtv.org
T: git git://linuxtv.org/media.git
F: drivers/media/radio/radio-keene*
+KERNEL API SPECIFICATION FRAMEWORK (KAPI)
+M: Sasha Levin <sashal@kernel.org>
+L: linux-api@vger.kernel.org
+S: Maintained
+F: Documentation/dev-tools/kernel-api-spec.rst
+F: include/linux/kernel_api_spec.h
+F: include/linux/syscall_api_spec.h
+F: kernel/api/
+F: tools/kapi/
+F: tools/lib/python/kdoc/kdoc_apispec.py
+
KERNEL AUTOMOUNTER
M: Ian Kent <raven@themaw.net>
L: autofs@vger.kernel.org
diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h
index 7e88705e907f4..2262b9d7ab097 100644
--- a/arch/x86/include/asm/syscall_wrapper.h
+++ b/arch/x86/include/asm/syscall_wrapper.h
@@ -7,6 +7,14 @@
#define _ASM_X86_SYSCALL_WRAPPER_H
#include <asm/ptrace.h>
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+struct kernel_api_spec;
+extern const struct kernel_api_spec *kapi_get_spec(const char *name);
+extern int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count);
+extern int kapi_validate_syscall_return(const struct kernel_api_spec *spec,
+ s64 retval);
+#endif
extern long __x64_sys_ni_syscall(const struct pt_regs *regs);
extern long __ia32_sys_ni_syscall(const struct pt_regs *regs);
@@ -220,6 +228,37 @@ extern long __ia32_sys_ni_syscall(const struct pt_regs *regs);
#endif /* CONFIG_COMPAT */
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+#define __SYSCALL_DEFINEx(x, name, ...) \
+ static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
+ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+ static inline long __do_kapi_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
+ __X64_SYS_STUBx(x, name, __VA_ARGS__) \
+ __IA32_SYS_STUBx(x, name, __VA_ARGS__) \
+ static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
+ { \
+ long ret = __do_kapi_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
+ __MAP(x,__SC_TEST,__VA_ARGS__); \
+ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
+ return ret; \
+ } \
+ static inline long __do_kapi_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\
+ { \
+ const struct kernel_api_spec *__spec = kapi_get_spec("sys" #name); \
+ if (__spec) { \
+ s64 __params[x] = { __MAP(x,__SC_CAST_TO_S64,__VA_ARGS__) }; \
+ int __ret = kapi_validate_syscall_params(__spec, __params, x); \
+ if (__ret) \
+ return __ret; \
+ } \
+ long ret = __do_sys##name(__MAP(x,__SC_ARGS,__VA_ARGS__)); \
+ if (__spec) { \
+ kapi_validate_syscall_return(__spec, (s64)ret); \
+ } \
+ return ret; \
+ } \
+ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+#else /* !CONFIG_KAPI_RUNTIME_CHECKS */
#define __SYSCALL_DEFINEx(x, name, ...) \
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
@@ -233,6 +272,7 @@ extern long __ia32_sys_ni_syscall(const struct pt_regs *regs);
return ret; \
} \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
/*
* As the generic SYSCALL_DEFINE0() macro does not decode any parameters for
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 1e1580febe4b9..60f2bb05b7bf3 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -296,6 +296,33 @@
#define TRACE_SYSCALLS()
#endif
+#ifdef CONFIG_KAPI_SPEC
+/*
+ * KAPI_SPECS - Include kernel API specifications in current section
+ *
+ * The .kapi_specs input section has 32-byte alignment requirement from
+ * the compiler, so we must align to 32 bytes before setting the start
+ * symbol to avoid padding between the symbol and actual data.
+ */
+#define KAPI_SPECS() \
+ . = ALIGN(32); \
+ __start_kapi_specs = .; \
+ KEEP(*(.kapi_specs)) \
+ __stop_kapi_specs = .;
+
+/* For placing KAPI specs in a dedicated section */
+#define KAPI_SPECS_SECTION() \
+ .kapi_specs : AT(ADDR(.kapi_specs) - LOAD_OFFSET) { \
+ . = ALIGN(32); \
+ __start_kapi_specs = .; \
+ KEEP(*(.kapi_specs)) \
+ __stop_kapi_specs = .; \
+ }
+#else
+#define KAPI_SPECS()
+#define KAPI_SPECS_SECTION()
+#endif
+
#ifdef CONFIG_BPF_EVENTS
#define BPF_RAW_TP() STRUCT_ALIGN(); \
BOUNDED_SECTION_BY(__bpf_raw_tp_map, __bpf_raw_tp)
@@ -485,6 +512,7 @@
. = ALIGN(8); \
BOUNDED_SECTION_BY(__tracepoints_ptrs, ___tracepoints_ptrs) \
*(__tracepoints_strings)/* Tracepoints: strings */ \
+ KAPI_SPECS() \
} \
\
.rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \
diff --git a/include/linux/kernel_api_spec.h b/include/linux/kernel_api_spec.h
new file mode 100644
index 0000000000000..445f3b83d7649
--- /dev/null
+++ b/include/linux/kernel_api_spec.h
@@ -0,0 +1,1580 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * kernel_api_spec.h - Kernel API Formal Specification Framework
+ *
+ * This framework provides structures and macros to formally specify kernel APIs
+ * in both human and machine-readable formats. It supports comprehensive documentation
+ * of function signatures, parameters, return values, error conditions, and constraints.
+ */
+
+#ifndef _LINUX_KERNEL_API_SPEC_H
+#define _LINUX_KERNEL_API_SPEC_H
+
+#include <linux/types.h>
+#include <linux/stringify.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+
+struct sigaction;
+
+#define KAPI_MAX_PARAMS 16
+#define KAPI_MAX_ERRORS 32
+#define KAPI_MAX_CONSTRAINTS 32
+#define KAPI_MAX_SIGNALS 32
+#define KAPI_MAX_NAME_LEN 128
+#define KAPI_MAX_DESC_LEN 512
+#define KAPI_MAX_CAPABILITIES 8
+#define KAPI_MAX_SOCKET_STATES 16
+#define KAPI_MAX_PROTOCOL_BEHAVIORS 8
+#define KAPI_MAX_NET_ERRORS 16
+#define KAPI_MAX_SOCKOPTS 16
+#define KAPI_MAX_ADDR_FAMILIES 8
+
+/* Magic numbers for section validation (ASCII mnemonics) */
+#define KAPI_MAGIC_PARAMS 0x4B415031 /* 'KAP1' */
+#define KAPI_MAGIC_RETURN 0x4B415232 /* 'KAR2' */
+#define KAPI_MAGIC_ERRORS 0x4B414533 /* 'KAE3' */
+#define KAPI_MAGIC_LOCKS 0x4B414C34 /* 'KAL4' */
+#define KAPI_MAGIC_CONSTRAINTS 0x4B414335 /* 'KAC5' */
+#define KAPI_MAGIC_INFO 0x4B414936 /* 'KAI6' */
+#define KAPI_MAGIC_SIGNALS 0x4B415337 /* 'KAS7' */
+#define KAPI_MAGIC_SIGMASK 0x4B414D38 /* 'KAM8' */
+#define KAPI_MAGIC_STRUCTS 0x4B415439 /* 'KAT9' */
+#define KAPI_MAGIC_EFFECTS 0x4B414641 /* 'KAFA' */
+#define KAPI_MAGIC_TRANS 0x4B415442 /* 'KATB' */
+#define KAPI_MAGIC_CAPS 0x4B414343 /* 'KACC' */
+
+/**
+ * enum kapi_param_type - Parameter type classification
+ * @KAPI_TYPE_VOID: void type
+ * @KAPI_TYPE_INT: Integer types (int, long, etc.)
+ * @KAPI_TYPE_UINT: Unsigned integer types
+ * @KAPI_TYPE_PTR: Pointer types
+ * @KAPI_TYPE_STRUCT: Structure types
+ * @KAPI_TYPE_UNION: Union types
+ * @KAPI_TYPE_ENUM: Enumeration types
+ * @KAPI_TYPE_FUNC_PTR: Function pointer types
+ * @KAPI_TYPE_ARRAY: Array types
+ * @KAPI_TYPE_FD: File descriptor - validated in process context
+ * @KAPI_TYPE_USER_PTR: User space pointer - validated for access and size
+ * @KAPI_TYPE_PATH: Pathname - validated for access and path limits
+ * @KAPI_TYPE_CUSTOM: Custom/complex types
+ */
+enum kapi_param_type {
+ KAPI_TYPE_VOID = 0,
+ KAPI_TYPE_INT,
+ KAPI_TYPE_UINT,
+ KAPI_TYPE_PTR,
+ KAPI_TYPE_STRUCT,
+ KAPI_TYPE_UNION,
+ KAPI_TYPE_ENUM,
+ KAPI_TYPE_FUNC_PTR,
+ KAPI_TYPE_ARRAY,
+ KAPI_TYPE_FD, /* File descriptor - validated in process context */
+ KAPI_TYPE_USER_PTR, /* User space pointer - validated for access and size */
+ KAPI_TYPE_PATH, /* Pathname - validated for access and path limits */
+ KAPI_TYPE_CUSTOM,
+};
+
+/**
+ * enum kapi_param_flags - Parameter attribute flags
+ * @KAPI_PARAM_IN: Input parameter
+ * @KAPI_PARAM_OUT: Output parameter
+ * @KAPI_PARAM_INOUT: Input/output parameter
+ * @KAPI_PARAM_OPTIONAL: Optional parameter (can be NULL)
+ * @KAPI_PARAM_CONST: Const qualified parameter
+ * @KAPI_PARAM_VOLATILE: Volatile qualified parameter
+ * @KAPI_PARAM_USER: User space pointer
+ * @KAPI_PARAM_DMA: DMA-capable memory required
+ * @KAPI_PARAM_ALIGNED: Alignment requirements
+ */
+enum kapi_param_flags {
+ KAPI_PARAM_IN = (1 << 0),
+ KAPI_PARAM_OUT = (1 << 1),
+ KAPI_PARAM_INOUT = (KAPI_PARAM_IN | KAPI_PARAM_OUT),
+ KAPI_PARAM_OPTIONAL = (1 << 3),
+ KAPI_PARAM_CONST = (1 << 4),
+ KAPI_PARAM_VOLATILE = (1 << 5),
+ KAPI_PARAM_USER = (1 << 6),
+ KAPI_PARAM_DMA = (1 << 7),
+ KAPI_PARAM_ALIGNED = (1 << 8),
+};
+
+/**
+ * enum kapi_context_flags - Function execution context flags
+ * @KAPI_CTX_PROCESS: Can be called from process context
+ * @KAPI_CTX_SOFTIRQ: Can be called from softirq context
+ * @KAPI_CTX_HARDIRQ: Can be called from hardirq context
+ * @KAPI_CTX_NMI: Can be called from NMI context
+ * @KAPI_CTX_ATOMIC: Must be called in atomic context
+ * @KAPI_CTX_SLEEPABLE: May sleep
+ * @KAPI_CTX_PREEMPT_DISABLED: Requires preemption disabled
+ * @KAPI_CTX_IRQ_DISABLED: Requires interrupts disabled
+ */
+enum kapi_context_flags {
+ KAPI_CTX_PROCESS = (1 << 0),
+ KAPI_CTX_SOFTIRQ = (1 << 1),
+ KAPI_CTX_HARDIRQ = (1 << 2),
+ KAPI_CTX_NMI = (1 << 3),
+ KAPI_CTX_ATOMIC = (1 << 4),
+ KAPI_CTX_SLEEPABLE = (1 << 5),
+ KAPI_CTX_PREEMPT_DISABLED = (1 << 6),
+ KAPI_CTX_IRQ_DISABLED = (1 << 7),
+};
+
+/**
+ * enum kapi_lock_type - Lock types used/required by the function
+ * @KAPI_LOCK_NONE: No locking requirements
+ * @KAPI_LOCK_MUTEX: Mutex lock
+ * @KAPI_LOCK_SPINLOCK: Spinlock
+ * @KAPI_LOCK_RWLOCK: Read-write lock
+ * @KAPI_LOCK_SEQLOCK: Sequence lock
+ * @KAPI_LOCK_RCU: RCU lock
+ * @KAPI_LOCK_SEMAPHORE: Semaphore
+ * @KAPI_LOCK_CUSTOM: Custom locking mechanism
+ */
+enum kapi_lock_type {
+ KAPI_LOCK_NONE = 0,
+ KAPI_LOCK_MUTEX,
+ KAPI_LOCK_SPINLOCK,
+ KAPI_LOCK_RWLOCK,
+ KAPI_LOCK_SEQLOCK,
+ KAPI_LOCK_RCU,
+ KAPI_LOCK_SEMAPHORE,
+ KAPI_LOCK_CUSTOM,
+};
+
+/**
+ * enum kapi_constraint_type - Types of parameter constraints
+ * @KAPI_CONSTRAINT_NONE: No constraint
+ * @KAPI_CONSTRAINT_RANGE: Numeric range constraint
+ * @KAPI_CONSTRAINT_MASK: Bitmask constraint
+ * @KAPI_CONSTRAINT_ENUM: Enumerated values constraint
+ * @KAPI_CONSTRAINT_ALIGNMENT: Alignment constraint (must be aligned to specified boundary)
+ * @KAPI_CONSTRAINT_POWER_OF_TWO: Value must be a power of two
+ * @KAPI_CONSTRAINT_PAGE_ALIGNED: Value must be page-aligned
+ * @KAPI_CONSTRAINT_NONZERO: Value must be non-zero
+ * @KAPI_CONSTRAINT_USER_STRING: Userspace null-terminated string with length range
+ * @KAPI_CONSTRAINT_USER_PATH: Userspace pathname string (validated for accessibility and PATH_MAX)
+ * @KAPI_CONSTRAINT_USER_PTR: Userspace pointer (validated for accessibility and size)
+ * @KAPI_CONSTRAINT_BUFFER: Userspace buffer pointer (validated by copy_to/from_user)
+ * @KAPI_CONSTRAINT_CUSTOM: Custom validation function
+ */
+enum kapi_constraint_type {
+ KAPI_CONSTRAINT_NONE = 0,
+ KAPI_CONSTRAINT_RANGE,
+ KAPI_CONSTRAINT_MASK,
+ KAPI_CONSTRAINT_ENUM,
+ KAPI_CONSTRAINT_ALIGNMENT,
+ KAPI_CONSTRAINT_POWER_OF_TWO,
+ KAPI_CONSTRAINT_PAGE_ALIGNED,
+ KAPI_CONSTRAINT_NONZERO,
+ KAPI_CONSTRAINT_USER_STRING,
+ KAPI_CONSTRAINT_USER_PATH,
+ KAPI_CONSTRAINT_USER_PTR,
+ KAPI_CONSTRAINT_BUFFER,
+ KAPI_CONSTRAINT_CUSTOM,
+};
+
+/**
+ * struct kapi_param_spec - Parameter specification
+ * @name: Parameter name
+ * @type_name: Type name as string
+ * @type: Parameter type classification
+ * @flags: Parameter attribute flags
+ * @size: Size in bytes (for arrays/buffers)
+ * @alignment: Required alignment
+ * @min_value: Minimum valid value (for numeric types)
+ * @max_value: Maximum valid value (for numeric types)
+ * @valid_mask: Valid bits mask (for flag parameters)
+ * @enum_values: Array of valid enumerated values
+ * @enum_count: Number of valid enumerated values
+ * @constraint_type: Type of constraint applied
+ * @validate: Custom validation function
+ * @description: Human-readable description
+ * @constraints: Additional constraints description
+ * @size_param_idx: Index of parameter that determines size (-1 if fixed size)
+ * @size_multiplier: Multiplier for size calculation (e.g., sizeof(struct))
+ */
+struct kapi_param_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ char type_name[KAPI_MAX_NAME_LEN];
+ enum kapi_param_type type;
+ u32 flags;
+ size_t size;
+ size_t alignment;
+ s64 min_value;
+ s64 max_value;
+ u64 valid_mask;
+ const s64 *enum_values;
+ u32 enum_count;
+ enum kapi_constraint_type constraint_type;
+ bool (*validate)(s64 value);
+ char description[KAPI_MAX_DESC_LEN];
+ char constraints[KAPI_MAX_DESC_LEN];
+ int size_param_idx; /* Index of param that determines size, -1 if N/A */
+ size_t size_multiplier; /* Size per unit (e.g., sizeof(struct epoll_event)) */
+} __packed;
+
+/**
+ * struct kapi_error_spec - Error condition specification
+ * @error_code: Error code value
+ * @name: Error code name (e.g., "EINVAL")
+ * @condition: Condition that triggers this error
+ * @description: Detailed error description
+ */
+struct kapi_error_spec {
+ int error_code;
+ char name[KAPI_MAX_NAME_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * enum kapi_return_check_type - Return value check types
+ * @KAPI_RETURN_EXACT: Success is an exact value
+ * @KAPI_RETURN_RANGE: Success is within a range
+ * @KAPI_RETURN_ERROR_CHECK: Success is when NOT in error list
+ * @KAPI_RETURN_FD: Return value is a file descriptor (>= 0 is success)
+ * @KAPI_RETURN_CUSTOM: Custom validation function
+ * @KAPI_RETURN_NO_RETURN: Function does not return (e.g., exec on success)
+ */
+enum kapi_return_check_type {
+ KAPI_RETURN_EXACT,
+ KAPI_RETURN_RANGE,
+ KAPI_RETURN_ERROR_CHECK,
+ KAPI_RETURN_FD,
+ KAPI_RETURN_CUSTOM,
+ KAPI_RETURN_NO_RETURN,
+};
+
+/**
+ * struct kapi_return_spec - Return value specification
+ * @type_name: Return type name
+ * @type: Return type classification
+ * @check_type: Type of success check to perform
+ * @success_value: Exact value indicating success (for EXACT)
+ * @success_min: Minimum success value (for RANGE)
+ * @success_max: Maximum success value (for RANGE)
+ * @error_values: Array of error values (for ERROR_CHECK)
+ * @error_count: Number of error values
+ * @is_success: Custom function to check success
+ * @description: Return value description
+ */
+struct kapi_return_spec {
+ char type_name[KAPI_MAX_NAME_LEN];
+ enum kapi_param_type type;
+ enum kapi_return_check_type check_type;
+ s64 success_value;
+ s64 success_min;
+ s64 success_max;
+ const s64 *error_values;
+ u32 error_count;
+ bool (*is_success)(s64 retval);
+ char description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * enum kapi_lock_scope - Lock acquisition/release scope
+ * @KAPI_LOCK_INTERNAL: Lock is acquired and released within the function (common case)
+ * @KAPI_LOCK_ACQUIRES: Function acquires lock but does not release it
+ * @KAPI_LOCK_RELEASES: Function releases lock (must be held on entry)
+ * @KAPI_LOCK_CALLER_HELD: Lock must be held by caller throughout the call
+ */
+enum kapi_lock_scope {
+ KAPI_LOCK_INTERNAL = 0,
+ KAPI_LOCK_ACQUIRES,
+ KAPI_LOCK_RELEASES,
+ KAPI_LOCK_CALLER_HELD,
+};
+
+/**
+ * struct kapi_lock_spec - Lock requirement specification
+ * @lock_name: Name of the lock
+ * @lock_type: Type of lock
+ * @scope: Lock scope (internal, acquires, releases, or caller-held)
+ * @description: Additional lock requirements
+ */
+struct kapi_lock_spec {
+ char lock_name[KAPI_MAX_NAME_LEN];
+ enum kapi_lock_type lock_type;
+ enum kapi_lock_scope scope;
+ char description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * struct kapi_constraint_spec - Additional constraint specification
+ * @name: Constraint name
+ * @description: Constraint description
+ * @expression: Formal expression (if applicable)
+ */
+struct kapi_constraint_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+ char expression[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * enum kapi_signal_direction - Signal flow direction
+ * @KAPI_SIGNAL_RECEIVE: Function may receive this signal
+ * @KAPI_SIGNAL_SEND: Function may send this signal
+ * @KAPI_SIGNAL_HANDLE: Function handles this signal specially
+ * @KAPI_SIGNAL_BLOCK: Function blocks this signal
+ * @KAPI_SIGNAL_IGNORE: Function ignores this signal
+ */
+enum kapi_signal_direction {
+ KAPI_SIGNAL_RECEIVE = (1 << 0),
+ KAPI_SIGNAL_SEND = (1 << 1),
+ KAPI_SIGNAL_HANDLE = (1 << 2),
+ KAPI_SIGNAL_BLOCK = (1 << 3),
+ KAPI_SIGNAL_IGNORE = (1 << 4),
+};
+
+/**
+ * enum kapi_signal_action - What the function does with the signal
+ * @KAPI_SIGNAL_ACTION_DEFAULT: Default signal action applies
+ * @KAPI_SIGNAL_ACTION_TERMINATE: Causes termination
+ * @KAPI_SIGNAL_ACTION_COREDUMP: Causes termination with core dump
+ * @KAPI_SIGNAL_ACTION_STOP: Stops the process
+ * @KAPI_SIGNAL_ACTION_CONTINUE: Continues a stopped process
+ * @KAPI_SIGNAL_ACTION_CUSTOM: Custom handling described in notes
+ * @KAPI_SIGNAL_ACTION_RETURN: Returns from syscall with EINTR
+ * @KAPI_SIGNAL_ACTION_RESTART: Restarts the syscall
+ * @KAPI_SIGNAL_ACTION_QUEUE: Queues the signal for later delivery
+ * @KAPI_SIGNAL_ACTION_DISCARD: Discards the signal
+ * @KAPI_SIGNAL_ACTION_TRANSFORM: Transforms to another signal
+ */
+enum kapi_signal_action {
+ KAPI_SIGNAL_ACTION_DEFAULT = 0,
+ KAPI_SIGNAL_ACTION_TERMINATE,
+ KAPI_SIGNAL_ACTION_COREDUMP,
+ KAPI_SIGNAL_ACTION_STOP,
+ KAPI_SIGNAL_ACTION_CONTINUE,
+ KAPI_SIGNAL_ACTION_CUSTOM,
+ KAPI_SIGNAL_ACTION_RETURN,
+ KAPI_SIGNAL_ACTION_RESTART,
+ KAPI_SIGNAL_ACTION_QUEUE,
+ KAPI_SIGNAL_ACTION_DISCARD,
+ KAPI_SIGNAL_ACTION_TRANSFORM,
+};
+
+/**
+ * struct kapi_signal_spec - Signal specification
+ * @signal_num: Signal number (e.g., SIGKILL, SIGTERM)
+ * @signal_name: Signal name as string
+ * @direction: Direction flags (OR of kapi_signal_direction)
+ * @action: What happens when signal is received
+ * @target: Description of target process/thread for sent signals
+ * @condition: Condition under which signal is sent/received/handled
+ * @description: Detailed description of signal handling
+ * @restartable: Whether syscall is restartable after this signal
+ * @sa_flags_required: Required signal action flags (SA_*)
+ * @sa_flags_forbidden: Forbidden signal action flags
+ * @error_on_signal: Error code returned when signal occurs (-EINTR, etc)
+ * @transform_to: Signal number to transform to (if action is TRANSFORM)
+ * @timing: When signal can occur ("entry", "during", "exit", "anytime")
+ * @priority: Signal handling priority (lower processed first)
+ * @interruptible: Whether this operation is interruptible by this signal
+ * @queue_behavior: How signal is queued ("realtime", "standard", "coalesce")
+ * @state_required: Required process state for signal to be delivered
+ * @state_forbidden: Forbidden process state for signal delivery
+ */
+struct kapi_signal_spec {
+ int signal_num;
+ char signal_name[32];
+ u32 direction;
+ enum kapi_signal_action action;
+ char target[KAPI_MAX_DESC_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+ bool restartable;
+ u32 sa_flags_required;
+ u32 sa_flags_forbidden;
+ int error_on_signal;
+ int transform_to;
+ char timing[32];
+ u8 priority;
+ bool interruptible;
+ char queue_behavior[128];
+ u32 state_required;
+ u32 state_forbidden;
+} __packed;
+
+/**
+ * struct kapi_signal_mask_spec - Signal mask specification
+ * @mask_name: Name of the signal mask
+ * @signals: Array of signal numbers in the mask
+ * @signal_count: Number of signals in the mask
+ * @description: Description of what this mask represents
+ */
+struct kapi_signal_mask_spec {
+ char mask_name[KAPI_MAX_NAME_LEN];
+ int signals[KAPI_MAX_SIGNALS];
+ u32 signal_count;
+ char description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * struct kapi_struct_field - Structure field specification
+ * @name: Field name
+ * @type: Field type classification
+ * @type_name: Type name as string
+ * @offset: Offset within structure
+ * @size: Size of field in bytes
+ * @flags: Field attribute flags
+ * @constraint_type: Type of constraint applied
+ * @min_value: Minimum valid value (for numeric types)
+ * @max_value: Maximum valid value (for numeric types)
+ * @valid_mask: Valid bits mask (for flag fields)
+ * @enum_values: Comma-separated list of valid enum values (for enum types)
+ * @description: Field description
+ */
+struct kapi_struct_field {
+ char name[KAPI_MAX_NAME_LEN];
+ enum kapi_param_type type;
+ char type_name[KAPI_MAX_NAME_LEN];
+ size_t offset;
+ size_t size;
+ u32 flags;
+ enum kapi_constraint_type constraint_type;
+ s64 min_value;
+ s64 max_value;
+ u64 valid_mask;
+ char enum_values[KAPI_MAX_DESC_LEN]; /* Comma-separated list of valid enum values */
+ char description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * struct kapi_struct_spec - Structure type specification
+ * @name: Structure name
+ * @size: Total size of structure
+ * @alignment: Required alignment
+ * @field_count: Number of fields
+ * @fields: Field specifications
+ * @description: Structure description
+ */
+struct kapi_struct_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ size_t size;
+ size_t alignment;
+ u32 field_count;
+ struct kapi_struct_field fields[KAPI_MAX_PARAMS];
+ char description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * enum kapi_capability_action - What the capability allows
+ * @KAPI_CAP_BYPASS_CHECK: Bypasses a check entirely
+ * @KAPI_CAP_INCREASE_LIMIT: Increases or removes a limit
+ * @KAPI_CAP_OVERRIDE_RESTRICTION: Overrides a restriction
+ * @KAPI_CAP_GRANT_PERMISSION: Grants permission that would otherwise be denied
+ * @KAPI_CAP_MODIFY_BEHAVIOR: Changes the behavior of the operation
+ * @KAPI_CAP_ACCESS_RESOURCE: Allows access to restricted resources
+ * @KAPI_CAP_PERFORM_OPERATION: Allows performing a privileged operation
+ */
+enum kapi_capability_action {
+ KAPI_CAP_BYPASS_CHECK = 0,
+ KAPI_CAP_INCREASE_LIMIT,
+ KAPI_CAP_OVERRIDE_RESTRICTION,
+ KAPI_CAP_GRANT_PERMISSION,
+ KAPI_CAP_MODIFY_BEHAVIOR,
+ KAPI_CAP_ACCESS_RESOURCE,
+ KAPI_CAP_PERFORM_OPERATION,
+};
+
+/**
+ * struct kapi_capability_spec - Capability requirement specification
+ * @capability: The capability constant (e.g., CAP_IPC_LOCK)
+ * @cap_name: Capability name as string
+ * @action: What the capability allows (kapi_capability_action)
+ * @allows: Description of what the capability allows
+ * @without_cap: What happens without the capability
+ * @check_condition: Condition when capability is checked
+ * @priority: Check priority (lower checked first)
+ * @alternative: Alternative capabilities that can be used
+ * @alternative_count: Number of alternative capabilities
+ */
+struct kapi_capability_spec {
+ int capability;
+ char cap_name[KAPI_MAX_NAME_LEN];
+ enum kapi_capability_action action;
+ char allows[KAPI_MAX_DESC_LEN];
+ char without_cap[KAPI_MAX_DESC_LEN];
+ char check_condition[KAPI_MAX_DESC_LEN];
+ u8 priority;
+ int alternative[KAPI_MAX_CAPABILITIES];
+ u32 alternative_count;
+} __packed;
+
+/**
+ * enum kapi_side_effect_type - Types of side effects
+ * @KAPI_EFFECT_NONE: No side effects
+ * @KAPI_EFFECT_ALLOC_MEMORY: Allocates memory
+ * @KAPI_EFFECT_FREE_MEMORY: Frees memory
+ * @KAPI_EFFECT_MODIFY_STATE: Modifies global/shared state
+ * @KAPI_EFFECT_SIGNAL_SEND: Sends signals
+ * @KAPI_EFFECT_FILE_POSITION: Modifies file position
+ * @KAPI_EFFECT_LOCK_ACQUIRE: Acquires locks
+ * @KAPI_EFFECT_LOCK_RELEASE: Releases locks
+ * @KAPI_EFFECT_RESOURCE_CREATE: Creates system resources (FDs, PIDs, etc)
+ * @KAPI_EFFECT_RESOURCE_DESTROY: Destroys system resources
+ * @KAPI_EFFECT_SCHEDULE: May cause scheduling/context switch
+ * @KAPI_EFFECT_HARDWARE: Interacts with hardware
+ * @KAPI_EFFECT_NETWORK: Network I/O operation
+ * @KAPI_EFFECT_FILESYSTEM: Filesystem modification
+ * @KAPI_EFFECT_PROCESS_STATE: Modifies process state
+ * @KAPI_EFFECT_IRREVERSIBLE: Effect cannot be undone
+ */
+enum kapi_side_effect_type {
+ KAPI_EFFECT_NONE = 0,
+ KAPI_EFFECT_ALLOC_MEMORY = (1 << 0),
+ KAPI_EFFECT_FREE_MEMORY = (1 << 1),
+ KAPI_EFFECT_MODIFY_STATE = (1 << 2),
+ KAPI_EFFECT_SIGNAL_SEND = (1 << 3),
+ KAPI_EFFECT_FILE_POSITION = (1 << 4),
+ KAPI_EFFECT_LOCK_ACQUIRE = (1 << 5),
+ KAPI_EFFECT_LOCK_RELEASE = (1 << 6),
+ KAPI_EFFECT_RESOURCE_CREATE = (1 << 7),
+ KAPI_EFFECT_RESOURCE_DESTROY = (1 << 8),
+ KAPI_EFFECT_SCHEDULE = (1 << 9),
+ KAPI_EFFECT_HARDWARE = (1 << 10),
+ KAPI_EFFECT_NETWORK = (1 << 11),
+ KAPI_EFFECT_FILESYSTEM = (1 << 12),
+ KAPI_EFFECT_PROCESS_STATE = (1 << 13),
+ KAPI_EFFECT_IRREVERSIBLE = (1 << 14),
+};
+
+/**
+ * struct kapi_side_effect - Side effect specification
+ * @type: Bitmask of effect types
+ * @target: What is affected (e.g., "process memory", "file descriptor table")
+ * @condition: Condition under which effect occurs
+ * @description: Detailed description of the effect
+ * @reversible: Whether the effect can be undone
+ */
+struct kapi_side_effect {
+ u32 type;
+ char target[KAPI_MAX_NAME_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+ bool reversible;
+} __packed;
+
+/**
+ * struct kapi_state_transition - State transition specification
+ * @from_state: Starting state description
+ * @to_state: Ending state description
+ * @condition: Condition for transition
+ * @object: Object whose state changes
+ * @description: Detailed description
+ */
+struct kapi_state_transition {
+ char from_state[KAPI_MAX_NAME_LEN];
+ char to_state[KAPI_MAX_NAME_LEN];
+ char condition[KAPI_MAX_DESC_LEN];
+ char object[KAPI_MAX_NAME_LEN];
+ char description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+#define KAPI_MAX_STRUCT_SPECS 8
+#define KAPI_MAX_SIDE_EFFECTS 32
+#define KAPI_MAX_STATE_TRANS 8
+
+/**
+ * enum kapi_socket_state - Socket states for state machine
+ */
+enum kapi_socket_state {
+ KAPI_SOCK_STATE_UNSPEC = 0,
+ KAPI_SOCK_STATE_CLOSED,
+ KAPI_SOCK_STATE_OPEN,
+ KAPI_SOCK_STATE_BOUND,
+ KAPI_SOCK_STATE_LISTEN,
+ KAPI_SOCK_STATE_SYN_SENT,
+ KAPI_SOCK_STATE_SYN_RECV,
+ KAPI_SOCK_STATE_ESTABLISHED,
+ KAPI_SOCK_STATE_FIN_WAIT1,
+ KAPI_SOCK_STATE_FIN_WAIT2,
+ KAPI_SOCK_STATE_CLOSE_WAIT,
+ KAPI_SOCK_STATE_CLOSING,
+ KAPI_SOCK_STATE_LAST_ACK,
+ KAPI_SOCK_STATE_TIME_WAIT,
+ KAPI_SOCK_STATE_CONNECTED,
+ KAPI_SOCK_STATE_DISCONNECTED,
+};
+
+/**
+ * enum kapi_socket_protocol - Socket protocol types
+ */
+enum kapi_socket_protocol {
+ KAPI_PROTO_TCP = (1 << 0),
+ KAPI_PROTO_UDP = (1 << 1),
+ KAPI_PROTO_UNIX = (1 << 2),
+ KAPI_PROTO_RAW = (1 << 3),
+ KAPI_PROTO_PACKET = (1 << 4),
+ KAPI_PROTO_NETLINK = (1 << 5),
+ KAPI_PROTO_SCTP = (1 << 6),
+ KAPI_PROTO_DCCP = (1 << 7),
+ KAPI_PROTO_ALL = 0xFFFFFFFF,
+};
+
+/**
+ * enum kapi_buffer_behavior - Network buffer handling behaviors
+ */
+enum kapi_buffer_behavior {
+ KAPI_BUF_PEEK = (1 << 0),
+ KAPI_BUF_TRUNCATE = (1 << 1),
+ KAPI_BUF_SCATTER = (1 << 2),
+ KAPI_BUF_ZERO_COPY = (1 << 3),
+ KAPI_BUF_KERNEL_ALLOC = (1 << 4),
+ KAPI_BUF_DMA_CAPABLE = (1 << 5),
+ KAPI_BUF_FRAGMENT = (1 << 6),
+};
+
+/**
+ * enum kapi_async_behavior - Asynchronous operation behaviors
+ */
+enum kapi_async_behavior {
+ KAPI_ASYNC_BLOCK = 0,
+ KAPI_ASYNC_NONBLOCK = (1 << 0),
+ KAPI_ASYNC_POLL_READY = (1 << 1),
+ KAPI_ASYNC_SIGNAL_DRIVEN = (1 << 2),
+ KAPI_ASYNC_AIO = (1 << 3),
+ KAPI_ASYNC_IO_URING = (1 << 4),
+ KAPI_ASYNC_EPOLL = (1 << 5),
+};
+
+/**
+ * struct kapi_socket_state_spec - Socket state requirement/transition
+ */
+struct kapi_socket_state_spec {
+ enum kapi_socket_state required_states[KAPI_MAX_SOCKET_STATES];
+ u32 required_state_count;
+ enum kapi_socket_state forbidden_states[KAPI_MAX_SOCKET_STATES];
+ u32 forbidden_state_count;
+ enum kapi_socket_state resulting_state;
+ char state_condition[KAPI_MAX_DESC_LEN];
+ u32 applicable_protocols;
+} __packed;
+
+/**
+ * struct kapi_protocol_behavior - Protocol-specific behavior
+ */
+struct kapi_protocol_behavior {
+ u32 applicable_protocols;
+ char behavior[KAPI_MAX_DESC_LEN];
+ s64 protocol_flags;
+ char flag_description[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * struct kapi_buffer_spec - Network buffer specification
+ */
+struct kapi_buffer_spec {
+ u32 buffer_behaviors;
+ size_t min_buffer_size;
+ size_t max_buffer_size;
+ size_t optimal_buffer_size;
+ char fragmentation_rules[KAPI_MAX_DESC_LEN];
+ bool can_partial_transfer;
+ char partial_transfer_rules[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * struct kapi_async_spec - Asynchronous behavior specification
+ */
+struct kapi_async_spec {
+ enum kapi_async_behavior supported_modes;
+ int nonblock_errno;
+ u32 poll_events_in;
+ u32 poll_events_out;
+ char completion_condition[KAPI_MAX_DESC_LEN];
+ bool supports_timeout;
+ char timeout_behavior[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/**
+ * struct kapi_addr_family_spec - Address family specification
+ */
+struct kapi_addr_family_spec {
+ int family;
+ char family_name[32];
+ size_t addr_struct_size;
+ size_t min_addr_len;
+ size_t max_addr_len;
+ char addr_format[KAPI_MAX_DESC_LEN];
+ bool supports_wildcard;
+ bool supports_multicast;
+ bool supports_broadcast;
+ char special_addresses[KAPI_MAX_DESC_LEN];
+ u32 port_range_min;
+ u32 port_range_max;
+} __packed;
+
+/**
+ * struct kernel_api_spec - Complete kernel API specification
+ * @name: Function name
+ * @version: API version
+ * @description: Brief description
+ * @long_description: Detailed description
+ * @context_flags: Execution context flags
+ * @param_count: Number of parameters
+ * @params: Parameter specifications
+ * @return_spec: Return value specification
+ * @error_count: Number of possible errors
+ * @errors: Error specifications
+ * @lock_count: Number of lock specifications
+ * @locks: Lock requirement specifications
+ * @constraint_count: Number of additional constraints
+ * @constraints: Additional constraint specifications
+ * @examples: Usage examples
+ * @notes: Additional notes
+ * @signal_count: Number of signal specifications
+ * @signals: Signal handling specifications
+ * @signal_mask_count: Number of signal mask specifications
+ * @signal_masks: Signal mask specifications
+ * @struct_spec_count: Number of structure specifications
+ * @struct_specs: Structure type specifications
+ * @side_effect_count: Number of side effect specifications
+ * @side_effects: Side effect specifications
+ * @state_trans_count: Number of state transition specifications
+ * @state_transitions: State transition specifications
+ */
+struct kernel_api_spec {
+ char name[KAPI_MAX_NAME_LEN];
+ u32 version;
+ char description[KAPI_MAX_DESC_LEN];
+ char long_description[KAPI_MAX_DESC_LEN * 4];
+ u32 context_flags;
+
+ /* Parameters */
+ u32 param_magic; /* 0x4B415031 = 'KAP1' */
+ u32 param_count;
+ struct kapi_param_spec params[KAPI_MAX_PARAMS];
+
+ /* Return value */
+ u32 return_magic; /* 0x4B415232 = 'KAR2' */
+ struct kapi_return_spec return_spec;
+
+ /* Errors */
+ u32 error_magic; /* 0x4B414533 = 'KAE3' */
+ u32 error_count;
+ struct kapi_error_spec errors[KAPI_MAX_ERRORS];
+
+ /* Locking */
+ u32 lock_magic; /* 0x4B414C34 = 'KAL4' */
+ u32 lock_count;
+ struct kapi_lock_spec locks[KAPI_MAX_CONSTRAINTS];
+
+ /* Constraints */
+ u32 constraint_magic; /* 0x4B414335 = 'KAC5' */
+ u32 constraint_count;
+ struct kapi_constraint_spec constraints[KAPI_MAX_CONSTRAINTS];
+
+ /* Additional information */
+ u32 info_magic; /* 0x4B414936 = 'KAI6' */
+ char examples[KAPI_MAX_DESC_LEN * 2];
+ char notes[KAPI_MAX_DESC_LEN * 2];
+
+ /* Signal specifications */
+ u32 signal_magic; /* 0x4B415337 = 'KAS7' */
+ u32 signal_count;
+ struct kapi_signal_spec signals[KAPI_MAX_SIGNALS];
+
+ /* Signal mask specifications */
+ u32 sigmask_magic; /* 0x4B414D38 = 'KAM8' */
+ u32 signal_mask_count;
+ struct kapi_signal_mask_spec signal_masks[KAPI_MAX_SIGNALS];
+
+ /* Structure specifications */
+ u32 struct_magic; /* 0x4B415439 = 'KAT9' */
+ u32 struct_spec_count;
+ struct kapi_struct_spec struct_specs[KAPI_MAX_STRUCT_SPECS];
+
+ /* Side effects */
+ u32 effect_magic; /* 0x4B414641 = 'KAFA' */
+ u32 side_effect_count;
+ struct kapi_side_effect side_effects[KAPI_MAX_SIDE_EFFECTS];
+
+ /* State transitions */
+ u32 trans_magic; /* 0x4B415442 = 'KATB' */
+ u32 state_trans_count;
+ struct kapi_state_transition state_transitions[KAPI_MAX_STATE_TRANS];
+
+ /* Capability specifications */
+ u32 cap_magic; /* 0x4B414343 = 'KACC' */
+ u32 capability_count;
+ struct kapi_capability_spec capabilities[KAPI_MAX_CAPABILITIES];
+
+ /* Extended fields for socket and network operations */
+ struct kapi_socket_state_spec socket_state;
+ struct kapi_protocol_behavior protocol_behaviors[KAPI_MAX_PROTOCOL_BEHAVIORS];
+ u32 protocol_behavior_count;
+ struct kapi_buffer_spec buffer_spec;
+ struct kapi_async_spec async_spec;
+ struct kapi_addr_family_spec addr_families[KAPI_MAX_ADDR_FAMILIES];
+ u32 addr_family_count;
+
+ /* Operation characteristics */
+ bool is_connection_oriented;
+ bool is_message_oriented;
+ bool supports_oob_data;
+ bool supports_peek;
+ bool supports_select_poll;
+ bool is_reentrant;
+
+ /* Semantic descriptions */
+ char connection_establishment[KAPI_MAX_DESC_LEN];
+ char connection_termination[KAPI_MAX_DESC_LEN];
+ char data_transfer_semantics[KAPI_MAX_DESC_LEN];
+} __packed;
+
+/* Macros for defining API specifications */
+
+/**
+ * DEFINE_KERNEL_API_SPEC - Define a kernel API specification
+ * @func_name: Function name to specify
+ */
+#define DEFINE_KERNEL_API_SPEC(func_name) \
+ static struct kernel_api_spec __kapi_spec_##func_name \
+ __used __section(".kapi_specs") = { \
+ .name = __stringify(func_name), \
+ .version = 1,
+
+#define KAPI_END_SPEC };
+
+/**
+ * KAPI_DESCRIPTION - Set API description
+ * @desc: Description string
+ */
+#define KAPI_DESCRIPTION(desc) \
+ .description = desc,
+
+/**
+ * KAPI_LONG_DESC - Set detailed API description
+ * @desc: Detailed description string
+ */
+#define KAPI_LONG_DESC(desc) \
+ .long_description = desc,
+
+/**
+ * KAPI_CONTEXT - Set execution context flags
+ * @flags: Context flags (OR'ed KAPI_CTX_* values)
+ */
+#define KAPI_CONTEXT(flags) \
+ .context_flags = flags,
+
+/**
+ * KAPI_PARAM - Define a parameter specification
+ * @idx: Parameter index (0-based)
+ * @pname: Parameter name
+ * @ptype: Type name string
+ * @pdesc: Parameter description
+ */
+#define KAPI_PARAM(idx, pname, ptype, pdesc) \
+ .params[idx] = { \
+ .name = pname, \
+ .type_name = ptype, \
+ .description = pdesc, \
+ .size_param_idx = -1, /* Default: no dynamic sizing */
+
+#define KAPI_PARAM_TYPE(ptype) \
+ .type = ptype,
+
+#define KAPI_PARAM_FLAGS(pflags) \
+ .flags = pflags,
+
+#define KAPI_PARAM_SIZE(psize) \
+ .size = psize,
+
+#define KAPI_PARAM_RANGE(pmin, pmax) \
+ .min_value = pmin, \
+ .max_value = pmax,
+
+#define KAPI_PARAM_CONSTRAINT_TYPE(ctype) \
+ .constraint_type = ctype,
+
+#define KAPI_PARAM_CONSTRAINT(desc) \
+ .constraints = desc,
+
+#define KAPI_PARAM_VALID_MASK(mask) \
+ .valid_mask = mask,
+
+#define KAPI_PARAM_ENUM_VALUES(values) \
+ .enum_values = values, \
+ .enum_count = ARRAY_SIZE(values),
+
+#define KAPI_PARAM_ALIGNMENT(align) \
+ .alignment = align,
+
+#define KAPI_PARAM_SIZE_PARAM(idx) \
+ .size_param_idx = idx,
+
+#define KAPI_PARAM_END },
+
+/**
+ * KAPI_PARAM_COUNT - Set the number of parameters
+ * @n: Number of parameters
+ */
+#define KAPI_PARAM_COUNT(n) \
+ .param_magic = KAPI_MAGIC_PARAMS, \
+ .param_count = n,
+
+/**
+ * KAPI_RETURN - Define return value specification
+ * @rtype: Return type name
+ * @rdesc: Return value description
+ */
+#define KAPI_RETURN(rtype, rdesc) \
+ .return_magic = KAPI_MAGIC_RETURN, \
+ .return_spec = { \
+ .type_name = rtype, \
+ .description = rdesc,
+
+#define KAPI_RETURN_SUCCESS(val, ...) \
+ .success_value = val,
+
+#define KAPI_RETURN_TYPE(rtype) \
+ .type = rtype,
+
+#define KAPI_RETURN_CHECK_TYPE(ctype) \
+ .check_type = ctype,
+
+#define KAPI_RETURN_ERROR_VALUES(values) \
+ .error_values = values,
+
+#define KAPI_RETURN_ERROR_COUNT(count) \
+ .error_count = count,
+
+#define KAPI_RETURN_SUCCESS_RANGE(min, max) \
+ .success_min = min, \
+ .success_max = max,
+
+#define KAPI_RETURN_END },
+
+/**
+ * KAPI_ERROR - Define an error condition
+ * @idx: Error index
+ * @ecode: Error code value
+ * @ename: Error name
+ * @econd: Error condition
+ * @edesc: Error description
+ */
+#define KAPI_ERROR(idx, ecode, ename, econd, edesc) \
+ .errors[idx] = { \
+ .error_code = ecode, \
+ .name = ename, \
+ .condition = econd, \
+ .description = edesc, \
+ },
+
+/**
+ * KAPI_ERROR_COUNT - Set the number of errors
+ * @n: Number of errors
+ */
+#define KAPI_ERROR_COUNT(n) \
+ .error_magic = KAPI_MAGIC_ERRORS, \
+ .error_count = n,
+
+/**
+ * KAPI_LOCK - Define a lock requirement
+ * @idx: Lock index
+ * @lname: Lock name
+ * @ltype: Lock type
+ */
+#define KAPI_LOCK(idx, lname, ltype) \
+ .locks[idx] = { \
+ .lock_name = lname, \
+ .lock_type = ltype,
+
+#define KAPI_LOCK_ACQUIRED \
+ .scope = KAPI_LOCK_ACQUIRES,
+
+#define KAPI_LOCK_RELEASED \
+ .scope = KAPI_LOCK_RELEASES,
+
+#define KAPI_LOCK_HELD_ENTRY \
+ .scope = KAPI_LOCK_CALLER_HELD,
+
+#define KAPI_LOCK_HELD_EXIT \
+ .scope = KAPI_LOCK_CALLER_HELD,
+
+#define KAPI_LOCK_DESC(ldesc) \
+ .description = ldesc,
+
+#define KAPI_LOCK_END },
+
+/**
+ * KAPI_CONSTRAINT - Define an additional constraint
+ * @idx: Constraint index
+ * @cname: Constraint name
+ * @cdesc: Constraint description
+ */
+#define KAPI_CONSTRAINT(idx, cname, cdesc) \
+ .constraints[idx] = { \
+ .name = cname, \
+ .description = cdesc,
+
+#define KAPI_CONSTRAINT_EXPR(expr) \
+ .expression = expr,
+
+#define KAPI_CONSTRAINT_END },
+
+/**
+ * KAPI_EXAMPLES - Set API usage examples
+ * @examples: Examples string
+ */
+#define KAPI_EXAMPLES(ex) \
+ .info_magic = KAPI_MAGIC_INFO, \
+ .examples = ex,
+
+/**
+ * KAPI_NOTES - Set API notes
+ * @notes: Notes string
+ */
+#define KAPI_NOTES(n) \
+ .notes = n,
+
+
+/**
+ * KAPI_SIGNAL - Define a signal specification
+ * @idx: Signal index
+ * @signum: Signal number (e.g., SIGKILL)
+ * @signame: Signal name string
+ * @dir: Direction flags
+ * @act: Action taken
+ */
+#define KAPI_SIGNAL(idx, signum, signame, dir, act) \
+ .signals[idx] = { \
+ .signal_num = signum, \
+ .signal_name = signame, \
+ .direction = dir, \
+ .action = act,
+
+#define KAPI_SIGNAL_TARGET(tgt) \
+ .target = tgt,
+
+#define KAPI_SIGNAL_CONDITION(cond) \
+ .condition = cond,
+
+#define KAPI_SIGNAL_DESC(desc) \
+ .description = desc,
+
+#define KAPI_SIGNAL_RESTARTABLE \
+ .restartable = true,
+
+#define KAPI_SIGNAL_SA_FLAGS_REQ(flags) \
+ .sa_flags_required = flags,
+
+#define KAPI_SIGNAL_SA_FLAGS_FORBID(flags) \
+ .sa_flags_forbidden = flags,
+
+#define KAPI_SIGNAL_ERROR(err) \
+ .error_on_signal = err,
+
+#define KAPI_SIGNAL_TRANSFORM(sig) \
+ .transform_to = sig,
+
+#define KAPI_SIGNAL_TIMING(when) \
+ .timing = when,
+
+#define KAPI_SIGNAL_PRIORITY(prio) \
+ .priority = prio,
+
+#define KAPI_SIGNAL_INTERRUPTIBLE \
+ .interruptible = true,
+
+#define KAPI_SIGNAL_QUEUE(behavior) \
+ .queue_behavior = behavior,
+
+#define KAPI_SIGNAL_STATE_REQ(state) \
+ .state_required = state,
+
+#define KAPI_SIGNAL_STATE_FORBID(state) \
+ .state_forbidden = state,
+
+#define KAPI_SIGNAL_END },
+
+#define KAPI_SIGNAL_COUNT(n) \
+ .signal_magic = KAPI_MAGIC_SIGNALS, \
+ .signal_count = n,
+
+/**
+ * KAPI_SIGNAL_MASK - Define a signal mask specification
+ * @idx: Mask index
+ * @name: Mask name
+ * @desc: Mask description
+ */
+#define KAPI_SIGNAL_MASK(idx, name, desc) \
+ .signal_masks[idx] = { \
+ .mask_name = name, \
+ .description = desc,
+
+/*
+ * KAPI_SIGNAL_MASK_SIGNALS - Specify signals in a signal mask
+ * @...: Variadic list of signal numbers
+ *
+ * Usage:
+ * KAPI_SIGNAL_MASK(0, "blocked", "Signals blocked during operation")
+ * KAPI_SIGNAL_MASK_SIGNALS(SIGINT, SIGTERM, SIGQUIT)
+ * KAPI_SIGNAL_MASK_END
+ */
+#define KAPI_SIGNAL_MASK_SIGNALS(...) \
+ .signals = { __VA_ARGS__ }, \
+ .signal_count = sizeof((int[]){ __VA_ARGS__ }) / sizeof(int),
+
+#define KAPI_SIGNAL_MASK_END },
+
+/**
+ * KAPI_STRUCT_SPEC - Define a structure specification
+ * @idx: Structure spec index
+ * @sname: Structure name
+ * @sdesc: Structure description
+ */
+#define KAPI_STRUCT_SPEC(idx, sname, sdesc) \
+ .struct_specs[idx] = { \
+ .name = #sname, \
+ .description = sdesc,
+
+#define KAPI_STRUCT_SIZE(ssize, salign) \
+ .size = ssize, \
+ .alignment = salign,
+
+#define KAPI_STRUCT_FIELD_COUNT(n) \
+ .field_count = n,
+
+/**
+ * KAPI_STRUCT_FIELD - Define a structure field
+ * @fidx: Field index
+ * @fname: Field name
+ * @ftype: Field type (KAPI_TYPE_*)
+ * @ftype_name: Type name as string
+ * @fdesc: Field description
+ */
+#define KAPI_STRUCT_FIELD(fidx, fname, ftype, ftype_name, fdesc) \
+ .fields[fidx] = { \
+ .name = fname, \
+ .type = ftype, \
+ .type_name = ftype_name, \
+ .description = fdesc,
+
+#define KAPI_FIELD_OFFSET(foffset) \
+ .offset = foffset,
+
+#define KAPI_FIELD_SIZE(fsize) \
+ .size = fsize,
+
+#define KAPI_FIELD_FLAGS(fflags) \
+ .flags = fflags,
+
+#define KAPI_FIELD_CONSTRAINT_RANGE(min, max) \
+ .constraint_type = KAPI_CONSTRAINT_RANGE, \
+ .min_value = min, \
+ .max_value = max,
+
+#define KAPI_FIELD_CONSTRAINT_MASK(mask) \
+ .constraint_type = KAPI_CONSTRAINT_MASK, \
+ .valid_mask = mask,
+
+#define KAPI_FIELD_CONSTRAINT_ENUM(values) \
+ .constraint_type = KAPI_CONSTRAINT_ENUM, \
+ .enum_values = values,
+
+#define KAPI_STRUCT_FIELD_END },
+
+#define KAPI_STRUCT_SPEC_END },
+
+/* Counter for structure specifications */
+#define KAPI_STRUCT_SPEC_COUNT(n) \
+ .struct_magic = KAPI_MAGIC_STRUCTS, \
+ .struct_spec_count = n,
+
+/* Additional lock-related macros */
+#define KAPI_LOCK_COUNT(n) \
+ .lock_magic = KAPI_MAGIC_LOCKS, \
+ .lock_count = n,
+
+/**
+ * KAPI_SIDE_EFFECT - Define a side effect
+ * @idx: Side effect index
+ * @etype: Effect type bitmask (OR'ed KAPI_EFFECT_* values)
+ * @etarget: What is affected
+ * @edesc: Effect description
+ */
+#define KAPI_SIDE_EFFECT(idx, etype, etarget, edesc) \
+ .side_effects[idx] = { \
+ .type = etype, \
+ .target = etarget, \
+ .description = edesc,
+
+#define KAPI_EFFECT_CONDITION(cond) \
+ .condition = cond,
+
+#define KAPI_EFFECT_REVERSIBLE \
+ .reversible = true,
+
+#define KAPI_SIDE_EFFECT_END },
+
+/**
+ * KAPI_STATE_TRANS - Define a state transition
+ * @idx: State transition index
+ * @obj: Object whose state changes
+ * @from: From state
+ * @to: To state
+ * @desc: Transition description
+ */
+#define KAPI_STATE_TRANS(idx, obj, from, to, desc) \
+ .state_transitions[idx] = { \
+ .object = obj, \
+ .from_state = from, \
+ .to_state = to, \
+ .description = desc,
+
+#define KAPI_STATE_TRANS_COND(cond) \
+ .condition = cond,
+
+#define KAPI_STATE_TRANS_END },
+
+/* Counters for side effects and state transitions */
+#define KAPI_SIDE_EFFECT_COUNT(n) \
+ .effect_magic = KAPI_MAGIC_EFFECTS, \
+ .side_effect_count = n,
+
+#define KAPI_STATE_TRANS_COUNT(n) \
+ .trans_magic = KAPI_MAGIC_TRANS, \
+ .state_trans_count = n,
+
+/* Helper macros for common side effect patterns */
+#define KAPI_EFFECTS_MEMORY (KAPI_EFFECT_ALLOC_MEMORY | KAPI_EFFECT_FREE_MEMORY)
+#define KAPI_EFFECTS_LOCKING (KAPI_EFFECT_LOCK_ACQUIRE | KAPI_EFFECT_LOCK_RELEASE)
+#define KAPI_EFFECTS_RESOURCES (KAPI_EFFECT_RESOURCE_CREATE | KAPI_EFFECT_RESOURCE_DESTROY)
+#define KAPI_EFFECTS_IO (KAPI_EFFECT_NETWORK | KAPI_EFFECT_FILESYSTEM)
+
+/*
+ * Helper macros for combining common parameter flag patterns.
+ * Note: KAPI_PARAM_IN, KAPI_PARAM_OUT, KAPI_PARAM_INOUT, and KAPI_PARAM_OPTIONAL
+ * are already defined in enum kapi_param_flags - use those directly.
+ */
+#define KAPI_PARAM_FLAGS_INOUT (KAPI_PARAM_IN | KAPI_PARAM_OUT)
+#define KAPI_PARAM_FLAGS_USER (KAPI_PARAM_USER | KAPI_PARAM_IN)
+
+/* Common signal timing constants */
+#define KAPI_SIGNAL_TIME_ENTRY "entry"
+#define KAPI_SIGNAL_TIME_DURING "during"
+#define KAPI_SIGNAL_TIME_EXIT "exit"
+#define KAPI_SIGNAL_TIME_ANYTIME "anytime"
+#define KAPI_SIGNAL_TIME_BLOCKING "while_blocked"
+#define KAPI_SIGNAL_TIME_SLEEPING "while_sleeping"
+#define KAPI_SIGNAL_TIME_BEFORE "before"
+#define KAPI_SIGNAL_TIME_AFTER "after"
+
+/* Common signal queue behaviors */
+#define KAPI_SIGNAL_QUEUE_STANDARD "standard"
+#define KAPI_SIGNAL_QUEUE_REALTIME "realtime"
+#define KAPI_SIGNAL_QUEUE_COALESCE "coalesce"
+#define KAPI_SIGNAL_QUEUE_REPLACE "replace"
+#define KAPI_SIGNAL_QUEUE_DISCARD "discard"
+
+/* Process state flags for signal delivery */
+#define KAPI_SIGNAL_STATE_RUNNING (1 << 0)
+#define KAPI_SIGNAL_STATE_SLEEPING (1 << 1)
+#define KAPI_SIGNAL_STATE_STOPPED (1 << 2)
+#define KAPI_SIGNAL_STATE_TRACED (1 << 3)
+#define KAPI_SIGNAL_STATE_ZOMBIE (1 << 4)
+#define KAPI_SIGNAL_STATE_DEAD (1 << 5)
+
+/* Capability specification macros */
+
+/**
+ * KAPI_CAPABILITY - Define a capability requirement
+ * @idx: Capability index
+ * @cap: Capability constant (e.g., CAP_IPC_LOCK)
+ * @name: Capability name string
+ * @act: Action type (kapi_capability_action)
+ */
+#define KAPI_CAPABILITY(idx, cap, name, act) \
+ .capabilities[idx] = { \
+ .capability = cap, \
+ .cap_name = name, \
+ .action = act,
+
+#define KAPI_CAP_ALLOWS(desc) \
+ .allows = desc,
+
+#define KAPI_CAP_WITHOUT(desc) \
+ .without_cap = desc,
+
+#define KAPI_CAP_CONDITION(cond) \
+ .check_condition = cond,
+
+#define KAPI_CAP_PRIORITY(prio) \
+ .priority = prio,
+
+#define KAPI_CAP_ALTERNATIVE(caps, count) \
+ .alternative = caps, \
+ .alternative_count = count,
+
+#define KAPI_CAPABILITY_END },
+
+/* Counter for capability specifications */
+#define KAPI_CAPABILITY_COUNT(n) \
+ .cap_magic = KAPI_MAGIC_CAPS, \
+ .capability_count = n,
+
+/* Common signal patterns for syscalls */
+#define KAPI_SIGNAL_INTERRUPTIBLE_SLEEP \
+ KAPI_SIGNAL(0, SIGINT, "SIGINT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_SLEEPING) \
+ KAPI_SIGNAL_ERROR(-EINTR) \
+ KAPI_SIGNAL_RESTARTABLE \
+ KAPI_SIGNAL_DESC("Interrupts sleep, returns -EINTR") \
+ KAPI_SIGNAL_END, \
+ KAPI_SIGNAL(1, SIGTERM, "SIGTERM", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_RETURN) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_SLEEPING) \
+ KAPI_SIGNAL_ERROR(-EINTR) \
+ KAPI_SIGNAL_RESTARTABLE \
+ KAPI_SIGNAL_DESC("Interrupts sleep, returns -EINTR") \
+ KAPI_SIGNAL_END
+
+#define KAPI_SIGNAL_FATAL_DEFAULT \
+ KAPI_SIGNAL(2, SIGKILL, "SIGKILL", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_TERMINATE) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_ANYTIME) \
+ KAPI_SIGNAL_PRIORITY(0) \
+ KAPI_SIGNAL_DESC("Process terminated immediately") \
+ KAPI_SIGNAL_END
+
+#define KAPI_SIGNAL_STOP_CONT \
+ KAPI_SIGNAL(3, SIGSTOP, "SIGSTOP", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_STOP) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_ANYTIME) \
+ KAPI_SIGNAL_DESC("Process stopped") \
+ KAPI_SIGNAL_END, \
+ KAPI_SIGNAL(4, SIGCONT, "SIGCONT", KAPI_SIGNAL_RECEIVE, KAPI_SIGNAL_ACTION_CONTINUE) \
+ KAPI_SIGNAL_TIMING(KAPI_SIGNAL_TIME_ANYTIME) \
+ KAPI_SIGNAL_DESC("Process continued") \
+ KAPI_SIGNAL_END
+
+/* Validation and runtime checking */
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value);
+bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+ s64 value, const s64 *all_params, int param_count);
+int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+ int param_idx, s64 value);
+int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count);
+bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval);
+bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval);
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval);
+void kapi_check_context(const struct kernel_api_spec *spec);
+#else
+static inline bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value)
+{
+ return true;
+}
+static inline bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+ s64 value, const s64 *all_params, int param_count)
+{
+ return true;
+}
+static inline int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+ int param_idx, s64 value)
+{
+ return 0;
+}
+static inline int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count)
+{
+ return 0;
+}
+static inline bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval)
+{
+ return true;
+}
+static inline bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval)
+{
+ return true;
+}
+static inline int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+ return 0;
+}
+static inline void kapi_check_context(const struct kernel_api_spec *spec) {}
+#endif
+
+/*
+ * Export/query functions
+ *
+ * kapi_get_spec() returns a pointer that is valid only while the caller can
+ * guarantee the spec is not concurrently unregistered (e.g., module unload).
+ * For static specs this is always safe; for dynamic specs callers must hold
+ * a reference or ensure the owning module is pinned.
+ */
+const struct kernel_api_spec *kapi_get_spec(const char *name);
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size);
+void kapi_print_spec(const struct kernel_api_spec *spec);
+
+/* Registration for dynamic APIs */
+int kapi_register_spec(struct kernel_api_spec *spec);
+void kapi_unregister_spec(const char *name);
+
+/* Helper to get parameter constraint info */
+static inline bool kapi_get_param_constraint(const char *api_name, int param_idx,
+ enum kapi_constraint_type *type,
+ u64 *valid_mask, s64 *min_val, s64 *max_val)
+{
+ const struct kernel_api_spec *spec;
+
+ might_sleep();
+ spec = kapi_get_spec(api_name);
+
+ if (!spec || param_idx >= spec->param_count)
+ return false;
+
+ if (type)
+ *type = spec->params[param_idx].constraint_type;
+ if (valid_mask)
+ *valid_mask = spec->params[param_idx].valid_mask;
+ if (min_val)
+ *min_val = spec->params[param_idx].min_value;
+ if (max_val)
+ *max_val = spec->params[param_idx].max_value;
+
+ return true;
+}
+
+/* Socket state requirement macros */
+#define KAPI_SOCKET_STATE_REQ(...) \
+ .socket_state = { \
+ .required_states = { __VA_ARGS__ }, \
+ .required_state_count = sizeof((enum kapi_socket_state[]){__VA_ARGS__})/sizeof(enum kapi_socket_state),
+
+#define KAPI_SOCKET_STATE_FORBID(...) \
+ .forbidden_states = { __VA_ARGS__ }, \
+ .forbidden_state_count = sizeof((enum kapi_socket_state[]){__VA_ARGS__})/sizeof(enum kapi_socket_state),
+
+#define KAPI_SOCKET_STATE_RESULT(state) \
+ .resulting_state = state,
+
+#define KAPI_SOCKET_STATE_COND(cond) \
+ .state_condition = cond,
+
+#define KAPI_SOCKET_STATE_PROTOS(protos) \
+ .applicable_protocols = protos,
+
+#define KAPI_SOCKET_STATE_END },
+
+/* Protocol behavior macros */
+#define KAPI_PROTOCOL_BEHAVIOR(idx, protos, desc) \
+ .protocol_behaviors[idx] = { \
+ .applicable_protocols = protos, \
+ .behavior = desc,
+
+#define KAPI_PROTOCOL_FLAGS(flags, desc) \
+ .protocol_flags = flags, \
+ .flag_description = desc,
+
+#define KAPI_PROTOCOL_BEHAVIOR_END },
+
+/* Async behavior macros */
+#define KAPI_ASYNC_SPEC(modes, errno) \
+ .async_spec = { \
+ .supported_modes = modes, \
+ .nonblock_errno = errno,
+
+#define KAPI_ASYNC_POLL(in, out) \
+ .poll_events_in = in, \
+ .poll_events_out = out,
+
+#define KAPI_ASYNC_COMPLETION(cond) \
+ .completion_condition = cond,
+
+#define KAPI_ASYNC_TIMEOUT(supported, desc) \
+ .supports_timeout = supported, \
+ .timeout_behavior = desc,
+
+#define KAPI_ASYNC_END },
+
+/* Buffer behavior macros */
+#define KAPI_BUFFER_SPEC(behaviors) \
+ .buffer_spec = { \
+ .buffer_behaviors = behaviors,
+
+#define KAPI_BUFFER_SIZE(min, max, optimal) \
+ .min_buffer_size = min, \
+ .max_buffer_size = max, \
+ .optimal_buffer_size = optimal,
+
+#define KAPI_BUFFER_PARTIAL(allowed, rules) \
+ .can_partial_transfer = allowed, \
+ .partial_transfer_rules = rules,
+
+#define KAPI_BUFFER_FRAGMENT(rules) \
+ .fragmentation_rules = rules,
+
+#define KAPI_BUFFER_END },
+
+/* Address family macros */
+#define KAPI_ADDR_FAMILY(idx, fam, name, struct_sz, min_len, max_len) \
+ .addr_families[idx] = { \
+ .family = fam, \
+ .family_name = name, \
+ .addr_struct_size = struct_sz, \
+ .min_addr_len = min_len, \
+ .max_addr_len = max_len,
+
+#define KAPI_ADDR_FORMAT(fmt) \
+ .addr_format = fmt,
+
+#define KAPI_ADDR_FEATURES(wildcard, multicast, broadcast) \
+ .supports_wildcard = wildcard, \
+ .supports_multicast = multicast, \
+ .supports_broadcast = broadcast,
+
+#define KAPI_ADDR_SPECIAL(addrs) \
+ .special_addresses = addrs,
+
+#define KAPI_ADDR_PORTS(min, max) \
+ .port_range_min = min, \
+ .port_range_max = max,
+
+#define KAPI_ADDR_FAMILY_END },
+
+#define KAPI_ADDR_FAMILY_COUNT(n) \
+ .addr_family_count = n,
+
+#define KAPI_PROTOCOL_BEHAVIOR_COUNT(n) \
+ .protocol_behavior_count = n,
+
+#define KAPI_CONSTRAINT_COUNT(n) \
+ .constraint_magic = KAPI_MAGIC_CONSTRAINTS, \
+ .constraint_count = n,
+
+/* Network operation characteristics macros */
+#define KAPI_NET_CONNECTION_ORIENTED \
+ .is_connection_oriented = true,
+
+#define KAPI_NET_MESSAGE_ORIENTED \
+ .is_message_oriented = true,
+
+#define KAPI_NET_SUPPORTS_OOB \
+ .supports_oob_data = true,
+
+#define KAPI_NET_SUPPORTS_PEEK \
+ .supports_peek = true,
+
+#define KAPI_NET_REENTRANT \
+ .is_reentrant = true,
+
+/* Semantic description macros */
+#define KAPI_NET_CONN_ESTABLISH(desc) \
+ .connection_establishment = desc,
+
+#define KAPI_NET_CONN_TERMINATE(desc) \
+ .connection_termination = desc,
+
+#define KAPI_NET_DATA_TRANSFER(desc) \
+ .data_transfer_semantics = desc,
+
+#endif /* _LINUX_KERNEL_API_SPEC_H */
diff --git a/include/linux/syscall_api_spec.h b/include/linux/syscall_api_spec.h
new file mode 100644
index 0000000000000..7fddb5ebbde6a
--- /dev/null
+++ b/include/linux/syscall_api_spec.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * syscall_api_spec.h - System Call API Specification Integration
+ *
+ * This header extends the SYSCALL_DEFINEX macros to support inline API specifications,
+ * allowing syscall documentation to be written alongside the implementation in a
+ * human-readable and machine-parseable format.
+ */
+
+#ifndef _LINUX_SYSCALL_API_SPEC_H
+#define _LINUX_SYSCALL_API_SPEC_H
+
+#include <linux/kernel_api_spec.h>
+
+/* Automatic syscall validation infrastructure */
+/*
+ * The validation is now integrated directly into the SYSCALL_DEFINEx macros
+ * in syscalls.h when CONFIG_KAPI_RUNTIME_CHECKS is enabled.
+ *
+ * The validation happens in the __do_kapi_sys##name wrapper function which:
+ * 1. Validates all parameters before calling the actual syscall
+ * 2. Calls the real syscall implementation
+ * 3. Validates the return value
+ * 4. Returns the result
+ */
+
+
+/*
+ * Helper macros for common syscall patterns
+ */
+
+/* For syscalls that can sleep */
+#define KAPI_SYSCALL_SLEEPABLE \
+ KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
+
+/* For syscalls that must be atomic */
+#define KAPI_SYSCALL_ATOMIC \
+ KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_ATOMIC)
+
+/* Common parameter specifications */
+#define KAPI_PARAM_FD(idx, desc) \
+ KAPI_PARAM(idx, "fd", "int", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_IN) \
+ .type = KAPI_TYPE_FD, \
+ .constraint_type = KAPI_CONSTRAINT_NONE, \
+ KAPI_PARAM_END
+
+#define KAPI_PARAM_USER_BUF(idx, name, desc) \
+ KAPI_PARAM(idx, name, "void __user *", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_USER | KAPI_PARAM_IN) \
+ KAPI_PARAM_END
+
+/**
+ * KAPI_PARAM_USER_STRUCT - Define a userspace struct pointer parameter
+ * @idx: Parameter index (0-based)
+ * @name: Parameter name
+ * @struct_type: The struct type (e.g., struct iocb)
+ * @desc: Parameter description
+ *
+ * This macro defines a parameter that is a userspace pointer to a struct.
+ * The pointer will be validated to ensure:
+ * - The pointer is accessible in userspace
+ * - The memory region of sizeof(struct_type) bytes is accessible
+ */
+#define KAPI_PARAM_USER_STRUCT(idx, name, struct_type, desc) \
+ KAPI_PARAM(idx, name, #struct_type " __user *", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_USER | KAPI_PARAM_IN) \
+ .type = KAPI_TYPE_USER_PTR, \
+ .size = sizeof(struct_type), \
+ .constraint_type = KAPI_CONSTRAINT_USER_PTR, \
+ KAPI_PARAM_END
+
+/**
+ * KAPI_PARAM_USER_PTR_SIZED - Define a userspace pointer with explicit size
+ * @idx: Parameter index (0-based)
+ * @name: Parameter name
+ * @ptr_size: Size in bytes of the memory region
+ * @desc: Parameter description
+ *
+ * This macro defines a parameter that is a userspace pointer to a memory
+ * region of a specific size. The pointer will be validated to ensure:
+ * - The pointer is accessible in userspace
+ * - The memory region of ptr_size bytes is accessible
+ */
+#define KAPI_PARAM_USER_PTR_SIZED(idx, name, ptr_size, desc) \
+ KAPI_PARAM(idx, name, "void __user *", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_USER | KAPI_PARAM_IN) \
+ .type = KAPI_TYPE_USER_PTR, \
+ .size = ptr_size, \
+ .constraint_type = KAPI_CONSTRAINT_USER_PTR, \
+ KAPI_PARAM_END
+
+/**
+ * KAPI_PARAM_USER_STRING - Define a userspace null-terminated string parameter
+ * @idx: Parameter index (0-based)
+ * @name: Parameter name
+ * @min_len: Minimum string length (excluding null terminator)
+ * @max_len: Maximum string length (excluding null terminator)
+ * @desc: Parameter description
+ *
+ * This macro defines a parameter that is a userspace pointer to a
+ * null-terminated string. The string will be validated to ensure:
+ * - The pointer is accessible in userspace
+ * - The string length (excluding null terminator) is within [min_len, max_len]
+ */
+#define KAPI_PARAM_USER_STRING(idx, name, min_len, max_len, desc) \
+ KAPI_PARAM(idx, name, "const char __user *", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_USER | KAPI_PARAM_IN) \
+ .type = KAPI_TYPE_USER_PTR, \
+ .constraint_type = KAPI_CONSTRAINT_USER_STRING, \
+ .min_value = min_len, \
+ .max_value = max_len, \
+ KAPI_PARAM_END
+
+/**
+ * KAPI_PARAM_USER_PATH - Define a userspace pathname parameter
+ * @idx: Parameter index (0-based)
+ * @name: Parameter name
+ * @desc: Parameter description
+ *
+ * This macro defines a parameter that is a userspace pointer to a
+ * null-terminated pathname string. The path will be validated to ensure:
+ * - The pointer is accessible in userspace
+ * - The path is a valid null-terminated string
+ * - The path length does not exceed PATH_MAX (4096 bytes)
+ */
+#define KAPI_PARAM_USER_PATH(idx, name, desc) \
+ KAPI_PARAM(idx, name, "const char __user *", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_USER | KAPI_PARAM_IN) \
+ .type = KAPI_TYPE_PATH, \
+ .constraint_type = KAPI_CONSTRAINT_USER_PATH, \
+ KAPI_PARAM_END
+
+#define KAPI_PARAM_SIZE_T(idx, name, desc) \
+ KAPI_PARAM(idx, name, "size_t", desc) \
+ KAPI_PARAM_FLAGS(KAPI_PARAM_IN) \
+ KAPI_PARAM_RANGE(0, S64_MAX) \
+ KAPI_PARAM_END
+
+/* Common error specifications */
+#define KAPI_ERROR_EBADF(idx) \
+ KAPI_ERROR(idx, -EBADF, "EBADF", "Invalid file descriptor", \
+ "The file descriptor is not valid or has been closed")
+
+#define KAPI_ERROR_EINVAL(idx, condition) \
+ KAPI_ERROR(idx, -EINVAL, "EINVAL", condition, \
+ "Invalid argument provided")
+
+#define KAPI_ERROR_ENOMEM(idx) \
+ KAPI_ERROR(idx, -ENOMEM, "ENOMEM", "Insufficient memory", \
+ "Cannot allocate memory for the operation")
+
+#define KAPI_ERROR_EPERM(idx) \
+ KAPI_ERROR(idx, -EPERM, "EPERM", "Operation not permitted", \
+ "The calling process does not have the required permissions")
+
+#define KAPI_ERROR_EFAULT(idx) \
+ KAPI_ERROR(idx, -EFAULT, "EFAULT", "Bad address", \
+ "Invalid user space address provided")
+
+/* Standard return value specifications */
+#define KAPI_RETURN_SUCCESS_ZERO \
+ KAPI_RETURN("long", "0 on success, negative error code on failure") \
+ KAPI_RETURN_SUCCESS(0, "== 0") \
+ KAPI_RETURN_END
+
+#define KAPI_RETURN_FD_SPEC \
+ KAPI_RETURN("long", "File descriptor on success, negative error code on failure") \
+ .check_type = KAPI_RETURN_FD, \
+ KAPI_RETURN_END
+
+#define KAPI_RETURN_COUNT \
+ KAPI_RETURN("long", "Number of bytes processed on success, negative error code on failure") \
+ KAPI_RETURN_SUCCESS(0, ">= 0") \
+ KAPI_RETURN_END
+
+/**
+ * KAPI_SIGNAL_MASK_COUNT - Set the signal mask count
+ * @count: Number of signal masks defined
+ */
+#define KAPI_SIGNAL_MASK_COUNT(count) \
+ .signal_mask_count = count,
+
+#endif /* _LINUX_SYSCALL_API_SPEC_H */
\ No newline at end of file
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 02bd6ddb62782..76758cc0f50d8 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -88,6 +88,7 @@ struct file_attr;
#include <linux/bug.h>
#include <linux/sem.h>
#include <asm/siginfo.h>
+#include <linux/syscall_api_spec.h>
#include <linux/unistd.h>
#include <linux/quota.h>
#include <linux/key.h>
@@ -133,6 +134,7 @@ struct file_attr;
#define __SC_TYPE(t, a) t
#define __SC_ARGS(t, a) a
#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))
+#define __SC_CAST_TO_S64(t, a) (s64)(a)
#ifdef CONFIG_FTRACE_SYSCALLS
#define __SC_STR_ADECL(t, a) #a
@@ -243,6 +245,42 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
* done within __do_sys_*().
*/
#ifndef __SYSCALL_DEFINEx
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+#define __SYSCALL_DEFINEx(x, name, ...) \
+ __diag_push(); \
+ __diag_ignore(GCC, 8, "-Wattribute-alias", \
+ "Type aliasing is used to sanitize syscall arguments");\
+ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
+ __attribute__((alias(__stringify(__se_sys##name)))); \
+ ALLOW_ERROR_INJECTION(sys##name, ERRNO); \
+ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+ static inline long __do_kapi_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
+ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
+ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
+ { \
+ long ret = __do_kapi_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
+ __MAP(x,__SC_TEST,__VA_ARGS__); \
+ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
+ return ret; \
+ } \
+ __diag_pop(); \
+ static inline long __do_kapi_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\
+ { \
+ const struct kernel_api_spec *__spec = kapi_get_spec("sys" #name); \
+ if (__spec) { \
+ s64 __params[x] = { __MAP(x,__SC_CAST_TO_S64,__VA_ARGS__) }; \
+ int __ret = kapi_validate_syscall_params(__spec, __params, x); \
+ if (__ret) \
+ return __ret; \
+ } \
+ long ret = __do_sys##name(__MAP(x,__SC_ARGS,__VA_ARGS__)); \
+ if (__spec) { \
+ kapi_validate_syscall_return(__spec, (s64)ret); \
+ } \
+ return ret; \
+ } \
+ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+#else /* !CONFIG_KAPI_RUNTIME_CHECKS */
#define __SYSCALL_DEFINEx(x, name, ...) \
__diag_push(); \
__diag_ignore(GCC, 8, "-Wattribute-alias", \
@@ -261,6 +299,7 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
} \
__diag_pop(); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
#endif /* __SYSCALL_DEFINEx */
/* For split 64-bit arguments on 32-bit architectures */
diff --git a/init/Kconfig b/init/Kconfig
index 444ce811ea674..bdfa64c240fa1 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2223,6 +2223,8 @@ source "kernel/Kconfig.kexec"
source "kernel/liveupdate/Kconfig"
+source "kernel/api/Kconfig"
+
endmenu # General setup
source "arch/Kconfig"
diff --git a/kernel/Makefile b/kernel/Makefile
index 6785982013dce..5643151536439 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -59,6 +59,9 @@ obj-y += dma/
obj-y += entry/
obj-y += unwind/
obj-$(CONFIG_MODULES) += module/
+obj-$(CONFIG_KAPI_SPEC) += api/
+# Ensure api/ is always cleaned even when CONFIG_KAPI_SPEC is not set
+obj- += api/
obj-$(CONFIG_KCMP) += kcmp.o
obj-$(CONFIG_FREEZER) += freezer.o
diff --git a/kernel/api/.gitignore b/kernel/api/.gitignore
new file mode 100644
index 0000000000000..ca2f632621cfc
--- /dev/null
+++ b/kernel/api/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+/generated_api_specs.c
diff --git a/kernel/api/Kconfig b/kernel/api/Kconfig
new file mode 100644
index 0000000000000..bdd655458dc2f
--- /dev/null
+++ b/kernel/api/Kconfig
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Kernel API Specification Framework Configuration
+#
+
+config KAPI_SPEC
+ bool "Kernel API Specification Framework"
+ default n
+ help
+ This option enables the kernel API specification framework,
+ which provides formal documentation of kernel APIs in both
+ human and machine-readable formats.
+
+ The framework allows developers to document APIs inline with
+ their implementation, including parameter specifications,
+ return values, error conditions, locking requirements, and
+ execution context constraints.
+
+ When enabled, API specifications can be queried at runtime
+ and exported in JSON format through debugfs.
+
+ If unsure, say N.
+
+config KAPI_RUNTIME_CHECKS
+ bool "Runtime API specification checks"
+ depends on KAPI_SPEC
+ depends on DEBUG_KERNEL
+ default n
+ help
+ Enable runtime validation of API usage against specifications.
+ This includes checking execution context requirements, parameter
+ validation, and lock state verification.
+
+ This adds overhead and should only be used for debugging and
+ development. The checks use WARN_ONCE to report violations.
+
+ If unsure, say N.
+
+config KAPI_SPEC_DEBUGFS
+ bool "Export kernel API specifications via debugfs"
+ depends on KAPI_SPEC
+ depends on DEBUG_FS
+ default n
+ help
+ This option enables exporting kernel API specifications through
+ the debugfs filesystem. When enabled, specifications can be
+ accessed at /sys/kernel/debug/kapi/.
+
+ The debugfs interface provides:
+ - A list of all available API specifications
+ - Detailed information for each API including parameters,
+ return values, errors, locking requirements, and constraints
+ - Complete machine-readable representation of the specs
+
+ This is useful for documentation tools, static analyzers, and
+ runtime introspection of kernel APIs.
+
+ If unsure, say N.
+
+config KAPI_KUNIT_TEST
+ tristate "KUnit tests for KAPI framework" if !KUNIT_ALL_TESTS
+ depends on KAPI_SPEC
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ KUnit tests for the Kernel API Specification Framework.
+ Tests registration, lookup, validation constraints, and
+ JSON export functionality.
+
+ If unsure, say N.
diff --git a/kernel/api/Makefile b/kernel/api/Makefile
new file mode 100644
index 0000000000000..c0a13fc590e4a
--- /dev/null
+++ b/kernel/api/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Kernel API Specification Framework
+#
+
+# Core API specification framework
+obj-$(CONFIG_KAPI_SPEC) += kernel_api_spec.o
+
+# Debugfs interface for kernel API specs
+obj-$(CONFIG_KAPI_SPEC_DEBUGFS) += kapi_debugfs.o
+
+# KUnit tests
+obj-$(CONFIG_KAPI_KUNIT_TEST) += kapi_kunit.o
+
diff --git a/kernel/api/kapi_kunit.c b/kernel/api/kapi_kunit.c
new file mode 100644
index 0000000000000..b4005c7121f7a
--- /dev/null
+++ b/kernel/api/kapi_kunit.c
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * KUnit tests for the Kernel API Specification Framework
+ *
+ * Tests registration, lookup, validation, and JSON export functionality.
+ */
+
+#include <kunit/test.h>
+#include <linux/kernel_api_spec.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+static void init_test_spec(struct kernel_api_spec *spec, const char *name)
+{
+ memset(spec, 0, sizeof(*spec));
+ strscpy(spec->name, name, KAPI_MAX_NAME_LEN);
+ spec->version = 1;
+ strscpy(spec->description, "Test API", KAPI_MAX_DESC_LEN);
+}
+
+/* Test 1: kapi_register_spec with valid spec returns 0 */
+static void test_register_valid(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ int ret;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_register_valid");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kapi_unregister_spec("test_register_valid");
+ kfree(spec);
+}
+
+/* Test 2: kapi_get_spec returns registered spec */
+static void test_lookup_registered(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ const struct kernel_api_spec *found;
+ int ret;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_lookup_func");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ found = kapi_get_spec("test_lookup_func");
+ KUNIT_EXPECT_PTR_EQ(test, found, (const struct kernel_api_spec *)spec);
+
+ kapi_unregister_spec("test_lookup_func");
+ kfree(spec);
+}
+
+/* Test 3: Double registration returns -EEXIST */
+static void test_double_register(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ int ret;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_double_reg");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kapi_register_spec(spec);
+ KUNIT_EXPECT_EQ(test, ret, -EEXIST);
+
+ kapi_unregister_spec("test_double_reg");
+ kfree(spec);
+}
+
+/* Test 4: Unregister makes spec unfindable */
+static void test_unregister(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ const struct kernel_api_spec *found;
+ int ret;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ init_test_spec(spec, "test_unreg_func");
+
+ ret = kapi_register_spec(spec);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ kapi_unregister_spec("test_unreg_func");
+
+ found = kapi_get_spec("test_unreg_func");
+ KUNIT_EXPECT_NULL(test, found);
+
+ kfree(spec);
+}
+
+/* Test 5: kapi_get_spec(NULL) returns NULL */
+static void test_get_spec_null(struct kunit *test)
+{
+ const struct kernel_api_spec *found;
+
+ found = kapi_get_spec(NULL);
+ KUNIT_EXPECT_NULL(test, found);
+}
+
+/* Test 6: kapi_register_spec(NULL) returns -EINVAL */
+static void test_register_null(struct kunit *test)
+{
+ int ret;
+
+ ret = kapi_register_spec(NULL);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/* Test 7: Spec with empty name is rejected */
+static void test_register_empty_name(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ int ret;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ /* name[0] == '\0' from kzalloc */
+ ret = kapi_register_spec(spec);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ kfree(spec);
+}
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+/* Test 8: RANGE constraint - value in range is valid */
+static void test_constraint_range_valid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_param", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_RANGE;
+ param.min_value = 0;
+ param.max_value = 100;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 50));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 100));
+}
+
+/* Test 9: RANGE constraint - value out of range is invalid */
+static void test_constraint_range_invalid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_param", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_RANGE;
+ param.min_value = 0;
+ param.max_value = 100;
+
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, -1));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 101));
+}
+
+/* Test 10: MASK constraint - valid bits pass */
+static void test_constraint_mask_valid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_flags", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_MASK;
+ param.valid_mask = 0xFF;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0x00));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0x0F));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0xFF));
+}
+
+/* Test 11: MASK constraint - extra bits fail */
+static void test_constraint_mask_invalid(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_flags", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_MASK;
+ param.valid_mask = 0xFF;
+
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0x100));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0x1FF));
+}
+
+/* Test 12: POWER_OF_TWO constraint */
+static void test_constraint_power_of_two(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_pot", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_POWER_OF_TWO;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 2));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 4));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 8));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 3));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 5));
+}
+
+/* Test 13: PAGE_ALIGNED constraint */
+static void test_constraint_page_aligned(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_page", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_PAGE_ALIGNED;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, PAGE_SIZE));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 2 * PAGE_SIZE));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, PAGE_SIZE - 1));
+}
+
+/* Test 14: NONZERO constraint */
+static void test_constraint_nonzero(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_nz", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_NONZERO;
+
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, -1));
+}
+
+/* Test 15: Return value validation - success */
+static void test_return_validation(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ strscpy(spec->name, "test_ret", sizeof(spec->name));
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_EXACT;
+ spec->return_spec.success_value = 0;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 0));
+}
+
+/* Test 16: Return value validation - known error */
+static void test_return_known_error(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ strscpy(spec->name, "test_ret_err", sizeof(spec->name));
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_FD;
+ spec->error_count = 1;
+ spec->errors[0].error_code = -ENOENT;
+ strscpy(spec->errors[0].name, "ENOENT", sizeof(spec->errors[0].name));
+
+ /* -ENOENT is in the error list, so it's valid */
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, -ENOENT));
+}
+
+/* Test 17: Return value validation - unknown error */
+static void test_return_unknown_error(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ strscpy(spec->name, "test_ret_unk", sizeof(spec->name));
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_FD;
+ spec->error_count = 1;
+ spec->errors[0].error_code = -ENOENT;
+ strscpy(spec->errors[0].name, "ENOENT", sizeof(spec->errors[0].name));
+
+ /* -EPERM is not in the error list, but unlisted errors are accepted
+ * since filesystem/device-specific errors may not be exhaustively listed
+ */
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, -EPERM));
+}
+
+/* Test 18: ALIGNMENT constraint */
+static void test_constraint_alignment(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_align", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_ALIGNMENT;
+ param.alignment = 8;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 8));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 16));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 7));
+}
+
+/* Test 19: FD validation rejects values > INT_MAX */
+static void test_fd_int_overflow(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_fd", sizeof(param.name));
+ param.type = KAPI_TYPE_FD;
+ param.constraint_type = KAPI_CONSTRAINT_NONE;
+
+ /* Value that overflows int: 0x100000003 -> truncates to 3 */
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0x100000003LL));
+}
+
+/* Test 23: ENUM constraint */
+static const s64 test_enum_vals[] = { 1, 5, 10 };
+
+static void test_constraint_enum(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_enum", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_ENUM;
+ param.enum_values = test_enum_vals;
+ param.enum_count = ARRAY_SIZE(test_enum_vals);
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 1));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 5));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 10));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 3));
+ KUNIT_EXPECT_FALSE(test, kapi_validate_param(¶m, 11));
+}
+
+/* Test 24: BUFFER constraint always accepts (size checked at runtime) */
+static void test_constraint_buffer(struct kunit *test)
+{
+ struct kapi_param_spec param = {};
+
+ strscpy(param.name, "test_buf", sizeof(param.name));
+ param.constraint_type = KAPI_CONSTRAINT_BUFFER;
+
+ /* Buffer constraint doesn't validate the value itself */
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_param(¶m, 4096));
+}
+
+/* Test 25: RETURN_RANGE check type */
+static void test_return_range(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ strscpy(spec->name, "test_ret_range", sizeof(spec->name));
+ spec->return_magic = KAPI_MAGIC_RETURN;
+ spec->return_spec.check_type = KAPI_RETURN_RANGE;
+ spec->return_spec.success_min = 0;
+ spec->return_spec.success_max = 100;
+
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 0));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 50));
+ KUNIT_EXPECT_TRUE(test, kapi_validate_return_value(spec, 100));
+}
+
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
+
+/* Test 26: Unregister non-existent spec is a no-op */
+static void test_unregister_nonexistent(struct kunit *test)
+{
+ /* Should not crash or error */
+ kapi_unregister_spec("nonexistent_spec_xyz");
+}
+
+/* Test 27: Multiple specs can be registered and looked up */
+static void test_multiple_specs(struct kunit *test)
+{
+ struct kernel_api_spec *spec1, *spec2;
+ const struct kernel_api_spec *found;
+
+ spec1 = kzalloc_obj(*spec1, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec1);
+ spec2 = kzalloc_obj(*spec2, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec2);
+
+ init_test_spec(spec1, "multi_spec_1");
+ init_test_spec(spec2, "multi_spec_2");
+
+ KUNIT_ASSERT_EQ(test, kapi_register_spec(spec1), 0);
+ KUNIT_ASSERT_EQ(test, kapi_register_spec(spec2), 0);
+
+ found = kapi_get_spec("multi_spec_1");
+ KUNIT_EXPECT_PTR_EQ(test, found, (const struct kernel_api_spec *)spec1);
+
+ found = kapi_get_spec("multi_spec_2");
+ KUNIT_EXPECT_PTR_EQ(test, found, (const struct kernel_api_spec *)spec2);
+
+ kapi_unregister_spec("multi_spec_1");
+ kapi_unregister_spec("multi_spec_2");
+ kfree(spec1);
+ kfree(spec2);
+}
+
+/* Test 20: JSON export produces valid output */
+static void test_json_export(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ char *buf;
+ int ret;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+
+ buf = kzalloc(4096, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
+
+ init_test_spec(spec, "test_json");
+ spec->param_count = 1;
+ strscpy(spec->params[0].name, "arg0", sizeof(spec->params[0].name));
+ strscpy(spec->params[0].type_name, "int", sizeof(spec->params[0].type_name));
+
+ ret = kapi_export_json(spec, buf, 4096);
+ KUNIT_EXPECT_GT(test, ret, 0);
+
+ /* Verify it starts with '{' and ends with '}' */
+ KUNIT_EXPECT_EQ(test, buf[0], '{');
+ KUNIT_ASSERT_GT(test, ret, 1);
+ /* Find last non-whitespace char */
+ while (ret > 0 && (buf[ret - 1] == '\n' || buf[ret - 1] == ' '))
+ ret--;
+ KUNIT_EXPECT_EQ(test, buf[ret - 1], '}');
+
+ /* Verify key fields are present */
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"name\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"test_json\""));
+ KUNIT_EXPECT_NOT_NULL(test, strstr(buf, "\"parameters\""));
+
+ kfree(buf);
+ kfree(spec);
+}
+
+/* Test 21: JSON export with NULL args returns -EINVAL */
+static void test_json_export_null(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ char buf[64];
+ int ret;
+
+ ret = kapi_export_json(NULL, buf, sizeof(buf));
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+ init_test_spec(spec, "test");
+
+ ret = kapi_export_json(spec, NULL, 64);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ ret = kapi_export_json(spec, buf, 0);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/* Test 22: JSON export with small buffer truncates gracefully */
+static void test_json_export_small_buffer(struct kunit *test)
+{
+ struct kernel_api_spec *spec;
+ char buf[64];
+ int ret;
+
+ spec = kunit_kzalloc(test, sizeof(*spec), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, spec);
+ init_test_spec(spec, "test_small");
+
+ ret = kapi_export_json(spec, buf, sizeof(buf));
+
+ /* Should return number of bytes written, buffer too small for full JSON */
+ KUNIT_EXPECT_GT(test, ret, 0);
+ KUNIT_EXPECT_LT(test, ret, (int)sizeof(buf));
+}
+
+static struct kunit_case kapi_test_cases[] = {
+ KUNIT_CASE(test_register_valid),
+ KUNIT_CASE(test_lookup_registered),
+ KUNIT_CASE(test_double_register),
+ KUNIT_CASE(test_unregister),
+ KUNIT_CASE(test_get_spec_null),
+ KUNIT_CASE(test_register_null),
+ KUNIT_CASE(test_register_empty_name),
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+ KUNIT_CASE(test_constraint_range_valid),
+ KUNIT_CASE(test_constraint_range_invalid),
+ KUNIT_CASE(test_constraint_mask_valid),
+ KUNIT_CASE(test_constraint_mask_invalid),
+ KUNIT_CASE(test_constraint_power_of_two),
+ KUNIT_CASE(test_constraint_page_aligned),
+ KUNIT_CASE(test_constraint_nonzero),
+ KUNIT_CASE(test_return_validation),
+ KUNIT_CASE(test_return_known_error),
+ KUNIT_CASE(test_return_unknown_error),
+ KUNIT_CASE(test_constraint_alignment),
+ KUNIT_CASE(test_fd_int_overflow),
+ KUNIT_CASE(test_constraint_enum),
+ KUNIT_CASE(test_constraint_buffer),
+ KUNIT_CASE(test_return_range),
+#endif
+ KUNIT_CASE(test_unregister_nonexistent),
+ KUNIT_CASE(test_multiple_specs),
+ KUNIT_CASE(test_json_export),
+ KUNIT_CASE(test_json_export_null),
+ KUNIT_CASE(test_json_export_small_buffer),
+ {}
+};
+
+static struct kunit_suite kapi_test_suite = {
+ .name = "kapi",
+ .test_cases = kapi_test_cases,
+};
+
+kunit_test_suite(kapi_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for Kernel API Specification Framework");
+MODULE_LICENSE("GPL");
diff --git a/kernel/api/kernel_api_spec.c b/kernel/api/kernel_api_spec.c
new file mode 100644
index 0000000000000..bf8fafe837435
--- /dev/null
+++ b/kernel/api/kernel_api_spec.c
@@ -0,0 +1,1277 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * kernel_api_spec.c - Kernel API Specification Framework Implementation
+ *
+ * Provides runtime support for kernel API specifications including validation,
+ * export to various formats, and querying capabilities.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kernel_api_spec.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/export.h>
+#include <linux/preempt.h>
+#include <linux/hardirq.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/uaccess.h>
+#include <linux/limits.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/ratelimit.h>
+
+/* Section where API specifications are stored */
+extern struct kernel_api_spec __start_kapi_specs[];
+extern struct kernel_api_spec __stop_kapi_specs[];
+
+/* Dynamic API registration */
+static LIST_HEAD(dynamic_api_specs);
+static DEFINE_MUTEX(api_spec_mutex);
+
+struct dynamic_api_spec {
+ struct list_head list;
+ struct kernel_api_spec *spec;
+};
+
+/*
+ * __kapi_find_spec_locked - Internal lookup, caller must hold api_spec_mutex
+ */
+static const struct kernel_api_spec *__kapi_find_spec_locked(const char *name)
+{
+ struct kernel_api_spec *spec;
+ struct dynamic_api_spec *dyn_spec;
+
+ /* Search static specifications */
+ for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) {
+ if (strcmp(spec->name, name) == 0)
+ return spec;
+ }
+
+ /* Search dynamic specifications (mutex already held) */
+ list_for_each_entry(dyn_spec, &dynamic_api_specs, list) {
+ if (strcmp(dyn_spec->spec->name, name) == 0)
+ return dyn_spec->spec;
+ }
+
+ return NULL;
+}
+
+/**
+ * kapi_get_spec - Get API specification by name
+ * @name: Function name to look up
+ *
+ * Return: Pointer to API specification or NULL if not found
+ */
+const struct kernel_api_spec *kapi_get_spec(const char *name)
+{
+ const struct kernel_api_spec *spec;
+
+ if (!name)
+ return NULL;
+
+ mutex_lock(&api_spec_mutex);
+ spec = __kapi_find_spec_locked(name);
+ mutex_unlock(&api_spec_mutex);
+
+ return spec;
+}
+EXPORT_SYMBOL_GPL(kapi_get_spec);
+
+/**
+ * kapi_register_spec - Register a dynamic API specification
+ * @spec: API specification to register
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int kapi_register_spec(struct kernel_api_spec *spec)
+{
+ struct dynamic_api_spec *dyn_spec;
+ int ret = 0;
+
+ if (!spec || !spec->name[0])
+ return -EINVAL;
+
+ dyn_spec = kzalloc(sizeof(*dyn_spec), GFP_KERNEL);
+ if (!dyn_spec)
+ return -ENOMEM;
+
+ dyn_spec->spec = spec;
+
+ mutex_lock(&api_spec_mutex);
+
+ /* Check if already exists while holding lock to prevent races */
+ if (__kapi_find_spec_locked(spec->name)) {
+ ret = -EEXIST;
+ kfree(dyn_spec);
+ } else {
+ list_add_tail(&dyn_spec->list, &dynamic_api_specs);
+ }
+
+ mutex_unlock(&api_spec_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kapi_register_spec);
+
+/**
+ * kapi_unregister_spec - Unregister a dynamic API specification
+ * @name: Name of API to unregister
+ */
+void kapi_unregister_spec(const char *name)
+{
+ struct dynamic_api_spec *dyn_spec, *tmp;
+
+ if (!name)
+ return;
+
+ mutex_lock(&api_spec_mutex);
+ list_for_each_entry_safe(dyn_spec, tmp, &dynamic_api_specs, list) {
+ if (strcmp(dyn_spec->spec->name, name) == 0) {
+ list_del(&dyn_spec->list);
+ kfree(dyn_spec);
+ break;
+ }
+ }
+ mutex_unlock(&api_spec_mutex);
+}
+EXPORT_SYMBOL_GPL(kapi_unregister_spec);
+
+/**
+ * param_type_to_string - Convert parameter type to string
+ * @type: Parameter type
+ *
+ * Return: String representation of type
+ */
+static const char *param_type_to_string(enum kapi_param_type type)
+{
+ static const char * const type_names[] = {
+ [KAPI_TYPE_VOID] = "void",
+ [KAPI_TYPE_INT] = "int",
+ [KAPI_TYPE_UINT] = "uint",
+ [KAPI_TYPE_PTR] = "pointer",
+ [KAPI_TYPE_STRUCT] = "struct",
+ [KAPI_TYPE_UNION] = "union",
+ [KAPI_TYPE_ENUM] = "enum",
+ [KAPI_TYPE_FUNC_PTR] = "function_pointer",
+ [KAPI_TYPE_ARRAY] = "array",
+ [KAPI_TYPE_FD] = "file_descriptor",
+ [KAPI_TYPE_USER_PTR] = "user_pointer",
+ [KAPI_TYPE_PATH] = "pathname",
+ [KAPI_TYPE_CUSTOM] = "custom",
+ };
+
+ if (type >= ARRAY_SIZE(type_names))
+ return "unknown";
+
+ return type_names[type];
+}
+
+/**
+ * lock_type_to_string - Convert lock type to string
+ * @type: Lock type
+ *
+ * Return: String representation of lock type
+ */
+static const char *lock_type_to_string(enum kapi_lock_type type)
+{
+ static const char * const lock_names[] = {
+ [KAPI_LOCK_NONE] = "none",
+ [KAPI_LOCK_MUTEX] = "mutex",
+ [KAPI_LOCK_SPINLOCK] = "spinlock",
+ [KAPI_LOCK_RWLOCK] = "rwlock",
+ [KAPI_LOCK_SEQLOCK] = "seqlock",
+ [KAPI_LOCK_RCU] = "rcu",
+ [KAPI_LOCK_SEMAPHORE] = "semaphore",
+ [KAPI_LOCK_CUSTOM] = "custom",
+ };
+
+ if (type >= ARRAY_SIZE(lock_names))
+ return "unknown";
+
+ return lock_names[type];
+}
+
+/**
+ * lock_scope_to_string - Convert lock scope to string
+ * @scope: Lock scope
+ *
+ * Return: String representation of lock scope
+ */
+static const char *lock_scope_to_string(enum kapi_lock_scope scope)
+{
+ static const char * const scope_names[] = {
+ [KAPI_LOCK_INTERNAL] = "internal",
+ [KAPI_LOCK_ACQUIRES] = "acquires",
+ [KAPI_LOCK_RELEASES] = "releases",
+ [KAPI_LOCK_CALLER_HELD] = "caller_held",
+ };
+
+ if (scope >= ARRAY_SIZE(scope_names))
+ return "unknown";
+
+ return scope_names[scope];
+}
+
+/**
+ * return_check_type_to_string - Convert return check type to string
+ * @type: Return check type
+ *
+ * Return: String representation of return check type
+ */
+static const char *return_check_type_to_string(enum kapi_return_check_type type)
+{
+ static const char * const check_names[] = {
+ [KAPI_RETURN_EXACT] = "exact",
+ [KAPI_RETURN_RANGE] = "range",
+ [KAPI_RETURN_ERROR_CHECK] = "error_check",
+ [KAPI_RETURN_FD] = "file_descriptor",
+ [KAPI_RETURN_CUSTOM] = "custom",
+ [KAPI_RETURN_NO_RETURN] = "no_return",
+ };
+
+ if (type >= ARRAY_SIZE(check_names))
+ return "unknown";
+
+ return check_names[type];
+}
+
+/**
+ * capability_action_to_string - Convert capability action to string
+ * @action: Capability action
+ *
+ * Return: String representation of capability action
+ */
+static const char *capability_action_to_string(enum kapi_capability_action action)
+{
+ static const char * const action_names[] = {
+ [KAPI_CAP_BYPASS_CHECK] = "bypass_check",
+ [KAPI_CAP_INCREASE_LIMIT] = "increase_limit",
+ [KAPI_CAP_OVERRIDE_RESTRICTION] = "override_restriction",
+ [KAPI_CAP_GRANT_PERMISSION] = "grant_permission",
+ [KAPI_CAP_MODIFY_BEHAVIOR] = "modify_behavior",
+ [KAPI_CAP_ACCESS_RESOURCE] = "access_resource",
+ [KAPI_CAP_PERFORM_OPERATION] = "perform_operation",
+ };
+
+ if (action >= ARRAY_SIZE(action_names))
+ return "unknown";
+
+ return action_names[action];
+}
+
+/*
+ * kapi_json_escape - Write a JSON-escaped string into a buffer
+ * @buf: Output buffer
+ * @size: Remaining space in buffer
+ * @str: Input string to escape
+ *
+ * Escapes backslash, double-quote, and control characters for JSON output.
+ * Return: Number of bytes written (via scnprintf semantics)
+ */
+static int kapi_json_escape(char *buf, size_t size, const char *str)
+{
+ int ret = 0;
+ const char *p;
+
+ if (!str || size == 0)
+ return 0;
+
+ for (p = str; *p && ret < size - 1; p++) {
+ switch (*p) {
+ case '\\':
+ ret += scnprintf(buf + ret, size - ret, "\\\\");
+ break;
+ case '"':
+ ret += scnprintf(buf + ret, size - ret, "\\\"");
+ break;
+ case '\n':
+ ret += scnprintf(buf + ret, size - ret, "\\n");
+ break;
+ case '\r':
+ ret += scnprintf(buf + ret, size - ret, "\\r");
+ break;
+ case '\t':
+ ret += scnprintf(buf + ret, size - ret, "\\t");
+ break;
+ default:
+ if ((unsigned char)*p < 0x20) {
+ ret += scnprintf(buf + ret, size - ret,
+ "\\u%04x", (unsigned char)*p);
+ } else {
+ ret += scnprintf(buf + ret, size - ret,
+ "%c", *p);
+ }
+ break;
+ }
+ }
+
+ if (ret < size)
+ buf[ret] = '\0';
+
+ return ret;
+}
+
+/* Helper to write a JSON-escaped string field */
+static int kapi_json_str(char *buf, size_t size, const char *str)
+{
+ int ret = 0;
+
+ ret += scnprintf(buf, size, "\"");
+ ret += kapi_json_escape(buf + ret, size - ret, str);
+ ret += scnprintf(buf + ret, size - ret, "\"");
+ return ret;
+}
+
+/**
+ * kapi_export_json - Export API specification to JSON format
+ * @spec: API specification to export
+ * @buf: Buffer to write JSON to
+ * @size: Size of buffer
+ *
+ * Return: Number of bytes written or negative error
+ */
+int kapi_export_json(const struct kernel_api_spec *spec, char *buf, size_t size)
+{
+ int ret = 0;
+ int i;
+
+ if (!spec || !buf || size == 0)
+ return -EINVAL;
+
+ ret = scnprintf(buf, size, "{\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"version\": %u,\n \"description\": ", spec->version);
+ ret += kapi_json_str(buf + ret, size - ret, spec->description);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"long_description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->long_description);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"context_flags\": \"0x%x\",\n", spec->context_flags);
+
+ /* Parameters */
+ ret += scnprintf(buf + ret, size - ret, " \"parameters\": [\n");
+
+ for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+ const struct kapi_param_spec *param = &spec->params[i];
+
+ ret += scnprintf(buf + ret, size - ret, " {\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, param->name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"type\": ");
+ ret += kapi_json_str(buf + ret, size - ret, param->type_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"type_class\": \"%s\","
+ "\n \"flags\": \"0x%x\","
+ "\n \"description\": ",
+ param_type_to_string(param->type),
+ param->flags);
+ ret += kapi_json_str(buf + ret, size - ret, param->description);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < spec->param_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Return value */
+ ret += scnprintf(buf + ret, size - ret, " \"return\": {\n \"type\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->return_spec.type_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"type_class\": \"%s\","
+ "\n \"check_type\": \"%s\",\n",
+ param_type_to_string(spec->return_spec.type),
+ return_check_type_to_string(spec->return_spec.check_type));
+
+ switch (spec->return_spec.check_type) {
+ case KAPI_RETURN_EXACT:
+ ret += scnprintf(buf + ret, size - ret,
+ " \"success_value\": %lld,\n",
+ spec->return_spec.success_value);
+ break;
+ case KAPI_RETURN_RANGE:
+ ret += scnprintf(buf + ret, size - ret,
+ " \"success_min\": %lld,\n"
+ " \"success_max\": %lld,\n",
+ spec->return_spec.success_min,
+ spec->return_spec.success_max);
+ break;
+ case KAPI_RETURN_ERROR_CHECK:
+ ret += scnprintf(buf + ret, size - ret,
+ " \"error_count\": %u,\n",
+ spec->return_spec.error_count);
+ break;
+ default:
+ break;
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->return_spec.description);
+ ret += scnprintf(buf + ret, size - ret, "\n },\n");
+
+ /* Errors */
+ ret += scnprintf(buf + ret, size - ret, " \"errors\": [\n");
+
+ for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+ const struct kapi_error_spec *error = &spec->errors[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n \"code\": %d,\n \"name\": ",
+ error->error_code);
+ ret += kapi_json_str(buf + ret, size - ret, error->name);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"condition\": ");
+ ret += kapi_json_str(buf + ret, size - ret, error->condition);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"description\": ");
+ ret += kapi_json_str(buf + ret, size - ret, error->description);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < spec->error_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Locks */
+ ret += scnprintf(buf + ret, size - ret, " \"locks\": [\n");
+
+ for (i = 0; i < spec->lock_count && i < KAPI_MAX_CONSTRAINTS; i++) {
+ const struct kapi_lock_spec *lock = &spec->locks[i];
+
+ ret += scnprintf(buf + ret, size - ret, " {\n \"name\": ");
+ ret += kapi_json_str(buf + ret, size - ret, lock->lock_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"type\": \"%s\","
+ "\n \"scope\": \"%s\","
+ "\n \"description\": ",
+ lock_type_to_string(lock->lock_type),
+ lock_scope_to_string(lock->scope));
+ ret += kapi_json_str(buf + ret, size - ret, lock->description);
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < spec->lock_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Capabilities */
+ ret += scnprintf(buf + ret, size - ret, " \"capabilities\": [\n");
+
+ for (i = 0; i < spec->capability_count && i < KAPI_MAX_CAPABILITIES; i++) {
+ const struct kapi_capability_spec *cap = &spec->capabilities[i];
+
+ ret += scnprintf(buf + ret, size - ret,
+ " {\n \"capability\": %d,\n \"name\": ",
+ cap->capability);
+ ret += kapi_json_str(buf + ret, size - ret, cap->cap_name);
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"action\": \"%s\",\n \"allows\": ",
+ capability_action_to_string(cap->action));
+ ret += kapi_json_str(buf + ret, size - ret, cap->allows);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"without_cap\": ");
+ ret += kapi_json_str(buf + ret, size - ret, cap->without_cap);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"check_condition\": ");
+ ret += kapi_json_str(buf + ret, size - ret, cap->check_condition);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"priority\": %u", cap->priority);
+
+ if (cap->alternative_count > 0) {
+ int j;
+
+ ret += scnprintf(buf + ret, size - ret,
+ ",\n \"alternatives\": [");
+ for (j = 0; j < cap->alternative_count; j++) {
+ ret += scnprintf(buf + ret, size - ret,
+ "%d%s", cap->alternative[j],
+ (j < cap->alternative_count - 1) ? ", " : "");
+ }
+ ret += scnprintf(buf + ret, size - ret, "]");
+ }
+
+ ret += scnprintf(buf + ret, size - ret,
+ "\n }%s\n",
+ (i < spec->capability_count - 1) ? "," : "");
+ }
+
+ ret += scnprintf(buf + ret, size - ret, " ],\n");
+
+ /* Additional info */
+ ret += scnprintf(buf + ret, size - ret, " \"examples\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->examples);
+ ret += scnprintf(buf + ret, size - ret, ",\n \"notes\": ");
+ ret += kapi_json_str(buf + ret, size - ret, spec->notes);
+ ret += scnprintf(buf + ret, size - ret, "\n}\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kapi_export_json);
+
+
+/**
+ * kapi_print_spec - Print API specification to kernel log
+ * @spec: API specification to print
+ */
+void kapi_print_spec(const struct kernel_api_spec *spec)
+{
+ int i;
+
+ if (!spec)
+ return;
+
+ pr_info("=== Kernel API Specification ===\n");
+ pr_info("Name: %s\n", spec->name);
+ pr_info("Version: %u\n", spec->version);
+ pr_info("Description: %s\n", spec->description);
+
+ if (spec->long_description[0])
+ pr_info("Long Description: %s\n", spec->long_description);
+
+ pr_info("Context Flags: 0x%x\n", spec->context_flags);
+
+ /* Parameters */
+ if (spec->param_count > 0) {
+ pr_info("Parameters:\n");
+ for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+ const struct kapi_param_spec *param = &spec->params[i];
+ pr_info(" [%d] %s: %s (flags: 0x%x)\n",
+ i, param->name, param->type_name, param->flags);
+ if (param->description[0])
+ pr_info(" Description: %s\n", param->description);
+ }
+ }
+
+ /* Return value */
+ pr_info("Return: %s\n", spec->return_spec.type_name);
+ if (spec->return_spec.description[0])
+ pr_info(" Description: %s\n", spec->return_spec.description);
+
+ /* Errors */
+ if (spec->error_count > 0) {
+ pr_info("Possible Errors:\n");
+ for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+ const struct kapi_error_spec *error = &spec->errors[i];
+ pr_info(" %s (%d): %s\n",
+ error->name, error->error_code, error->condition);
+ }
+ }
+
+ /* Capabilities */
+ if (spec->capability_count > 0) {
+ pr_info("Capabilities:\n");
+ for (i = 0; i < spec->capability_count && i < KAPI_MAX_CAPABILITIES; i++) {
+ const struct kapi_capability_spec *cap = &spec->capabilities[i];
+ pr_info(" %s (%d):\n", cap->cap_name, cap->capability);
+ pr_info(" Action: %s\n", capability_action_to_string(cap->action));
+ pr_info(" Allows: %s\n", cap->allows);
+ pr_info(" Without: %s\n", cap->without_cap);
+ if (cap->check_condition[0])
+ pr_info(" Condition: %s\n", cap->check_condition);
+ }
+ }
+
+ pr_info("================================\n");
+}
+EXPORT_SYMBOL_GPL(kapi_print_spec);
+
+#ifdef CONFIG_KAPI_RUNTIME_CHECKS
+
+/**
+ * kapi_validate_fd - Validate that a file descriptor value is in valid range
+ * @fd: File descriptor to validate
+ *
+ * Only checks the numeric range, not whether the fd is currently open.
+ * Checking openness would be TOCTOU (the fd can be closed between check
+ * and use) and incorrect for close() (which should accept any fd, returning
+ * EBADF for invalid ones). The kernel's own syscall handling validates
+ * actual fd state.
+ *
+ * Return: true if fd is in valid range, false otherwise
+ */
+static bool kapi_validate_fd(int fd)
+{
+ /* Special case: AT_FDCWD is always valid */
+ if (fd == AT_FDCWD)
+ return true;
+
+ /* Check basic range - negative fds are invalid */
+ if (fd < 0)
+ return false;
+
+ return true;
+}
+
+/**
+ * kapi_validate_user_ptr - Validate that a user pointer is accessible
+ * @ptr: User pointer to validate
+ * @size: Size in bytes to validate
+ *
+ * Return: true if user memory is accessible, false otherwise
+ */
+static bool kapi_validate_user_ptr(const void __user *ptr, size_t size)
+{
+ /* NULL pointers are not valid; caller handles optional case */
+ if (!ptr)
+ return false;
+
+ return access_ok(ptr, size);
+}
+
+/**
+ * kapi_validate_user_ptr_with_params - Validate user pointer with dynamic size
+ * @param_spec: Parameter specification
+ * @ptr: User pointer to validate
+ * @all_params: Array of all parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: true if user memory is accessible, false otherwise
+ */
+static bool kapi_validate_user_ptr_with_params(const struct kapi_param_spec *param_spec,
+ const void __user *ptr,
+ const s64 *all_params,
+ int param_count)
+{
+ size_t actual_size;
+
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ /* Calculate actual size based on related parameter */
+ if (param_spec->size_param_idx >= 0 &&
+ param_spec->size_param_idx < param_count) {
+ s64 count = all_params[param_spec->size_param_idx];
+
+ /* Validate count is non-negative */
+ if (count < 0) {
+ pr_warn_ratelimited("Parameter %s: size determinant is negative (%lld)\n",
+ param_spec->name, count);
+ return false;
+ }
+
+ /* Check for multiplication overflow */
+ if (param_spec->size_multiplier > 0 &&
+ count > SIZE_MAX / param_spec->size_multiplier) {
+ pr_warn_ratelimited("Parameter %s: size calculation overflow\n",
+ param_spec->name);
+ return false;
+ }
+
+ actual_size = (size_t)count * param_spec->size_multiplier;
+ } else {
+ /* Use fixed size */
+ actual_size = param_spec->size;
+ }
+
+ return kapi_validate_user_ptr(ptr, actual_size);
+}
+
+/**
+ * kapi_validate_path - Validate that a pathname is accessible and within limits
+ * @path: User pointer to pathname
+ * @param_spec: Parameter specification
+ *
+ * Return: true if path is valid, false otherwise
+ */
+static bool kapi_validate_path(const char __user *path,
+ const struct kapi_param_spec *param_spec)
+{
+ size_t len;
+
+ /* NULL is allowed for optional parameters */
+ if (!path && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!path) {
+ pr_warn_ratelimited("Parameter %s: NULL path not allowed\n", param_spec->name);
+ return false;
+ }
+
+ /* Check if the path is accessible */
+ if (!access_ok(path, 1)) {
+ pr_warn_ratelimited("Parameter %s: path pointer %p not accessible\n",
+ param_spec->name, path);
+ return false;
+ }
+
+ /*
+ * Use strnlen_user to check the path length and accessibility.
+ * Note: strnlen_user() is subject to TOCTOU -- the measured length
+ * may change if another thread modifies the user memory. This is
+ * acceptable since the kernel re-copies and re-validates the path
+ * later in the syscall path. This check is best-effort.
+ */
+ len = strnlen_user(path, PATH_MAX + 1);
+ if (len == 0) {
+ pr_warn_ratelimited("Parameter %s: invalid path pointer %p\n",
+ param_spec->name, path);
+ return false;
+ }
+
+ /* Check path length limit */
+ if (len > PATH_MAX) {
+ pr_warn_ratelimited("Parameter %s: path too long (exceeds PATH_MAX)\n",
+ param_spec->name);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * kapi_validate_user_string - Validate a userspace null-terminated string
+ * @str: User pointer to string
+ * @param_spec: Parameter specification containing length constraints
+ *
+ * Validates that the userspace string pointer is accessible and that the
+ * string length (excluding null terminator) is within the range specified
+ * by min_value and max_value in the parameter specification.
+ *
+ * Return: true if string is valid, false otherwise
+ */
+static bool kapi_validate_user_string(const char __user *str,
+ const struct kapi_param_spec *param_spec)
+{
+ size_t len;
+ size_t max_check_len;
+
+ /* NULL is allowed for optional parameters */
+ if (!str && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!str) {
+ pr_warn_ratelimited("Parameter %s: NULL string not allowed\n", param_spec->name);
+ return false;
+ }
+
+ /* Check if the string pointer is accessible */
+ if (!access_ok(str, 1)) {
+ pr_warn_ratelimited("Parameter %s: string pointer %p not accessible\n",
+ param_spec->name, str);
+ return false;
+ }
+
+ /*
+ * Use strnlen_user to check the string length and validate accessibility.
+ * Check up to max_value + 1 to detect strings that are too long.
+ * If max_value is 0 or unset, use PATH_MAX as a reasonable default.
+ *
+ * Note: strnlen_user() is subject to TOCTOU -- see comment in
+ * kapi_validate_path() above. This check is best-effort.
+ */
+ max_check_len = param_spec->max_value > 0 ?
+ (size_t)param_spec->max_value + 1 : PATH_MAX + 1;
+ len = strnlen_user(str, max_check_len);
+
+ if (len == 0) {
+ pr_warn_ratelimited("Parameter %s: invalid string pointer %p\n",
+ param_spec->name, str);
+ return false;
+ }
+
+ /*
+ * strnlen_user returns the length including the null terminator.
+ * Convert to string length (excluding terminator) for range check.
+ */
+ len--;
+
+ /* Check minimum length constraint */
+ if (param_spec->min_value > 0 && len < (size_t)param_spec->min_value) {
+ pr_warn_ratelimited("Parameter %s: string too short (%zu < %lld)\n",
+ param_spec->name, len, param_spec->min_value);
+ return false;
+ }
+
+ /* Check maximum length constraint */
+ if (param_spec->max_value > 0 && len > (size_t)param_spec->max_value) {
+ pr_warn_ratelimited("Parameter %s: string too long (%zu > %lld)\n",
+ param_spec->name, len, param_spec->max_value);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * kapi_validate_user_ptr_constraint - Validate a userspace pointer with size
+ * @ptr: User pointer to validate
+ * @param_spec: Parameter specification containing size
+ *
+ * Validates that the userspace pointer is accessible and that the memory
+ * region of the specified size can be accessed. The size is taken from
+ * the param_spec->size field.
+ *
+ * Return: true if pointer is valid, false otherwise
+ */
+static bool kapi_validate_user_ptr_constraint(const void __user *ptr,
+ const struct kapi_param_spec *param_spec)
+{
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!ptr) {
+ pr_warn_ratelimited("Parameter %s: NULL pointer not allowed\n", param_spec->name);
+ return false;
+ }
+
+ /* Validate size is specified */
+ if (param_spec->size == 0) {
+ pr_warn_ratelimited("Parameter %s: size not specified for user pointer validation\n",
+ param_spec->name);
+ return false;
+ }
+
+ /* Check if the memory region is accessible */
+ if (!access_ok(ptr, param_spec->size)) {
+ pr_warn_ratelimited("Parameter %s: user pointer %p not accessible for %zu bytes\n",
+ param_spec->name, ptr, param_spec->size);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * kapi_validate_param - Validate a parameter against its specification
+ * @param_spec: Parameter specification
+ * @value: Parameter value to validate
+ *
+ * Return: true if valid, false otherwise
+ */
+bool kapi_validate_param(const struct kapi_param_spec *param_spec, s64 value)
+{
+ int i;
+
+ /* Special handling for file descriptor type */
+ if (param_spec->type == KAPI_TYPE_FD) {
+ if (value < INT_MIN || value > INT_MAX) {
+ pr_warn_ratelimited("Parameter %s: file descriptor %lld out of int range\n",
+ param_spec->name, value);
+ return false;
+ }
+ if (!kapi_validate_fd((int)value)) {
+ pr_warn_ratelimited("Parameter %s: invalid file descriptor %lld\n",
+ param_spec->name, value);
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ /* Special handling for user pointer type */
+ if (param_spec->type == KAPI_TYPE_USER_PTR) {
+ const void __user *ptr = (const void __user *)value;
+
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!kapi_validate_user_ptr(ptr, param_spec->size)) {
+ pr_warn_ratelimited("Parameter %s: invalid user pointer %p (size: %zu)\n",
+ param_spec->name, ptr, param_spec->size);
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ /* Special handling for path type */
+ if (param_spec->type == KAPI_TYPE_PATH) {
+ const char __user *path = (const char __user *)value;
+
+ if (!kapi_validate_path(path, param_spec)) {
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ switch (param_spec->constraint_type) {
+ case KAPI_CONSTRAINT_NONE:
+ case KAPI_CONSTRAINT_BUFFER:
+ return true;
+
+ case KAPI_CONSTRAINT_RANGE:
+ /*
+ * If max_value is negative, it was likely set from an unsigned
+ * constant (e.g. SIZE_MAX) that overflowed s64. Treat as no
+ * upper bound — only check the minimum.
+ */
+ if (param_spec->max_value >= 0) {
+ if (value < param_spec->min_value ||
+ value > param_spec->max_value) {
+ pr_warn_ratelimited("Parameter %s value %lld out of range [%lld, %lld]\n",
+ param_spec->name, value,
+ param_spec->min_value,
+ param_spec->max_value);
+ return false;
+ }
+ } else {
+ if (value < param_spec->min_value) {
+ pr_warn_ratelimited("Parameter %s value %lld below minimum %lld\n",
+ param_spec->name, value,
+ param_spec->min_value);
+ return false;
+ }
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_MASK:
+ if (value & ~param_spec->valid_mask) {
+ pr_warn_ratelimited("Parameter %s value 0x%llx contains invalid bits (valid mask: 0x%llx)\n",
+ param_spec->name, value, param_spec->valid_mask);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_ENUM:
+ if (!param_spec->enum_values || param_spec->enum_count == 0)
+ return true;
+
+ for (i = 0; i < param_spec->enum_count; i++) {
+ if (value == param_spec->enum_values[i])
+ return true;
+ }
+ pr_warn_ratelimited("Parameter %s value %lld not in valid enumeration\n",
+ param_spec->name, value);
+ return false;
+
+ case KAPI_CONSTRAINT_ALIGNMENT:
+ if (param_spec->alignment == 0) {
+ pr_warn_ratelimited("Parameter %s: alignment constraint specified but alignment is 0\n",
+ param_spec->name);
+ return false;
+ }
+ if (param_spec->alignment & (param_spec->alignment - 1)) {
+ pr_warn_ratelimited("Parameter %s: alignment %zu is not a power of two\n",
+ param_spec->name, param_spec->alignment);
+ return false;
+ }
+ if (value & (param_spec->alignment - 1)) {
+ pr_warn_ratelimited("Parameter %s value 0x%llx not aligned to %zu boundary\n",
+ param_spec->name, value, param_spec->alignment);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_POWER_OF_TWO:
+ if (value == 0 || (value & (value - 1))) {
+ pr_warn_ratelimited("Parameter %s value %lld is not a power of two\n",
+ param_spec->name, value);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_PAGE_ALIGNED:
+ if (value & (PAGE_SIZE - 1)) {
+ pr_warn_ratelimited("Parameter %s value 0x%llx not page-aligned (PAGE_SIZE=%ld)\n",
+ param_spec->name, value, PAGE_SIZE);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_NONZERO:
+ if (value == 0) {
+ pr_warn_ratelimited("Parameter %s must be non-zero\n", param_spec->name);
+ return false;
+ }
+ return true;
+
+ case KAPI_CONSTRAINT_USER_STRING:
+ return kapi_validate_user_string((const char __user *)value, param_spec);
+
+ case KAPI_CONSTRAINT_USER_PATH:
+ return kapi_validate_path((const char __user *)value, param_spec);
+
+ case KAPI_CONSTRAINT_USER_PTR:
+ return kapi_validate_user_ptr_constraint((const void __user *)value, param_spec);
+
+ case KAPI_CONSTRAINT_CUSTOM:
+ if (param_spec->validate)
+ return param_spec->validate(value);
+ return true;
+
+ default:
+ return true;
+ }
+}
+EXPORT_SYMBOL_GPL(kapi_validate_param);
+
+/**
+ * kapi_validate_param_with_context - Validate parameter with access to all params
+ * @param_spec: Parameter specification
+ * @value: Parameter value to validate
+ * @all_params: Array of all parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: true if valid, false otherwise
+ */
+bool kapi_validate_param_with_context(const struct kapi_param_spec *param_spec,
+ s64 value, const s64 *all_params, int param_count)
+{
+ /* Special handling for user pointer type with dynamic sizing */
+ if (param_spec->type == KAPI_TYPE_USER_PTR) {
+ const void __user *ptr = (const void __user *)value;
+
+ /* NULL is allowed for optional parameters */
+ if (!ptr && (param_spec->flags & KAPI_PARAM_OPTIONAL))
+ return true;
+
+ if (!kapi_validate_user_ptr_with_params(param_spec, ptr, all_params, param_count)) {
+ pr_warn_ratelimited("Parameter %s: invalid user pointer %p\n",
+ param_spec->name, ptr);
+ return false;
+ }
+ /* Continue with additional constraint checks if needed */
+ }
+
+ /* For other types, fall back to regular validation */
+ return kapi_validate_param(param_spec, value);
+}
+EXPORT_SYMBOL_GPL(kapi_validate_param_with_context);
+
+/**
+ * kapi_validate_syscall_param - Validate syscall parameter with enforcement
+ * @spec: API specification
+ * @param_idx: Parameter index
+ * @value: Parameter value
+ *
+ * Return: -EINVAL if invalid, 0 if valid
+ */
+int kapi_validate_syscall_param(const struct kernel_api_spec *spec,
+ int param_idx, s64 value)
+{
+ const struct kapi_param_spec *param_spec;
+
+ if (!spec || param_idx < 0 || param_idx >= spec->param_count)
+ return 0;
+
+ param_spec = &spec->params[param_idx];
+
+ if (!kapi_validate_param(param_spec, value)) {
+ if (strncmp(spec->name, "sys_", 4) == 0) {
+ /* For syscalls, we can return EINVAL to userspace */
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_param);
+
+/**
+ * kapi_validate_syscall_params - Validate all syscall parameters together
+ * @spec: API specification
+ * @params: Array of parameter values
+ * @param_count: Number of parameters
+ *
+ * Return: -EINVAL if any parameter is invalid, 0 if all valid
+ */
+int kapi_validate_syscall_params(const struct kernel_api_spec *spec,
+ const s64 *params, int param_count)
+{
+ int i;
+
+ if (!spec || !params)
+ return 0;
+
+ /* Validate that we have the expected number of parameters */
+ if (param_count != spec->param_count) {
+ pr_warn_ratelimited("API %s: parameter count mismatch (expected %u, got %d)\n",
+ spec->name, spec->param_count, param_count);
+ return -EINVAL;
+ }
+
+ /* Validate each parameter with context */
+ for (i = 0; i < spec->param_count && i < KAPI_MAX_PARAMS; i++) {
+ const struct kapi_param_spec *param_spec = &spec->params[i];
+
+ if (!kapi_validate_param_with_context(param_spec, params[i], params, param_count)) {
+ if (strncmp(spec->name, "sys_", 4) == 0) {
+ /* For syscalls, we can return EINVAL to userspace */
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_params);
+
+/**
+ * kapi_check_return_success - Check if return value indicates success
+ * @return_spec: Return specification
+ * @retval: Return value to check
+ *
+ * Returns true if the return value indicates success according to the spec.
+ */
+bool kapi_check_return_success(const struct kapi_return_spec *return_spec, s64 retval)
+{
+ u32 i;
+
+ if (!return_spec)
+ return true; /* No spec means we can't validate */
+
+ switch (return_spec->check_type) {
+ case KAPI_RETURN_EXACT:
+ return retval == return_spec->success_value;
+
+ case KAPI_RETURN_RANGE:
+ return retval >= return_spec->success_min &&
+ retval <= return_spec->success_max;
+
+ case KAPI_RETURN_ERROR_CHECK:
+ /* Success if NOT in error list */
+ if (return_spec->error_values) {
+ for (i = 0; i < return_spec->error_count; i++) {
+ if (retval == return_spec->error_values[i])
+ return false; /* Found in error list */
+ }
+ }
+ return true; /* Not in error list = success */
+
+ case KAPI_RETURN_FD:
+ /* File descriptors: >= 0 is success, < 0 is error */
+ return retval >= 0;
+
+ case KAPI_RETURN_CUSTOM:
+ if (return_spec->is_success)
+ return return_spec->is_success(retval);
+ fallthrough;
+
+ default:
+ return true; /* Unknown check type, assume success */
+ }
+}
+EXPORT_SYMBOL_GPL(kapi_check_return_success);
+
+/**
+ * kapi_validate_return_value - Validate that return value matches spec
+ * @spec: API specification
+ * @retval: Return value to validate
+ *
+ * Return: true if return value is valid according to spec, false otherwise.
+ *
+ * This function checks:
+ * 1. If the value indicates success, it must match the success criteria
+ * 2. If the value indicates error, it must be one of the specified error codes
+ */
+bool kapi_validate_return_value(const struct kernel_api_spec *spec, s64 retval)
+{
+ int i;
+ bool is_success;
+
+ if (!spec)
+ return true; /* No spec means we can't validate */
+
+ /* First check if this is a success return */
+ is_success = kapi_check_return_success(&spec->return_spec, retval);
+
+ if (is_success) {
+ /* Special validation for file descriptor returns */
+ if (spec->return_spec.check_type == KAPI_RETURN_FD) {
+ /* For successful FD returns, validate it's a valid FD */
+ if (retval > INT_MAX || !kapi_validate_fd((int)retval)) {
+ pr_warn_ratelimited("API %s returned invalid file descriptor %lld\n",
+ spec->name, retval);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* Error case - check if it's one of the specified errors */
+ if (spec->error_count == 0) {
+ /* No errors specified, so any error is potentially valid */
+ pr_debug("API %s returned unspecified error %lld\n",
+ spec->name, retval);
+ return true;
+ }
+
+ /* Check if the error is in our list of specified errors */
+ for (i = 0; i < spec->error_count && i < KAPI_MAX_ERRORS; i++) {
+ if (retval == spec->errors[i].error_code)
+ return true;
+ }
+
+ /*
+ * Error not in spec - log at debug level since filesystem-specific and
+ * device-specific error codes may not be exhaustively listed.
+ */
+ pr_debug("API %s returned error code %lld not listed in spec\n",
+ spec->name, retval);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_return_value);
+
+/**
+ * kapi_validate_syscall_return - Validate syscall return value with enforcement
+ * @spec: API specification
+ * @retval: Return value
+ *
+ * Return: 0 if valid, -EINVAL if the return value doesn't match spec
+ *
+ * For syscalls, this can help detect kernel bugs where unspecified error
+ * codes are returned to userspace.
+ */
+int kapi_validate_syscall_return(const struct kernel_api_spec *spec, s64 retval)
+{
+ if (!spec)
+ return 0;
+
+ /* Skip return validation if return spec was not defined */
+ if (spec->return_magic != KAPI_MAGIC_RETURN)
+ return 0;
+
+ if (!kapi_validate_return_value(spec, retval)) {
+ /* Log the violation but don't change the return value */
+ pr_warn_ratelimited("KAPI: Syscall %s returned unspecified value %lld\n",
+ spec->name, retval);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kapi_validate_syscall_return);
+
+/**
+ * kapi_check_context - Check if current context matches API requirements
+ * @spec: API specification to check against
+ */
+void kapi_check_context(const struct kernel_api_spec *spec)
+{
+ bool valid = false;
+ u32 ctx;
+
+ if (!spec)
+ return;
+
+ ctx = spec->context_flags;
+
+ if (!ctx)
+ return;
+
+ /* Check if we're in an allowed context */
+ if ((ctx & KAPI_CTX_PROCESS) && !in_interrupt())
+ valid = true;
+
+ if ((ctx & KAPI_CTX_SOFTIRQ) && in_softirq())
+ valid = true;
+
+ if ((ctx & KAPI_CTX_HARDIRQ) && in_hardirq())
+ valid = true;
+
+ if ((ctx & KAPI_CTX_NMI) && in_nmi())
+ valid = true;
+
+ if (!valid) {
+ WARN_ONCE(1, "API %s called from invalid context\n", spec->name);
+ }
+
+ /* Check specific requirements */
+ if ((ctx & KAPI_CTX_ATOMIC) && preemptible()) {
+ WARN_ONCE(1, "API %s requires atomic context\n", spec->name);
+ }
+
+ if ((ctx & KAPI_CTX_SLEEPABLE) && !preemptible()) {
+ WARN_ONCE(1, "API %s requires sleepable context\n", spec->name);
+ }
+}
+EXPORT_SYMBOL_GPL(kapi_check_context);
+
+#endif /* CONFIG_KAPI_RUNTIME_CHECKS */
diff --git a/scripts/generate_api_specs.sh b/scripts/generate_api_specs.sh
new file mode 100755
index 0000000000000..2c3078a508fef
--- /dev/null
+++ b/scripts/generate_api_specs.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Stub script for generating API specifications collector
+# This is a placeholder until the full implementation is available
+#
+
+cat << 'EOF'
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Auto-generated API specifications collector (stub)
+ * Generated by scripts/generate_api_specs.sh
+ */
+
+#include <linux/kernel_api_spec.h>
+
+/* No API specifications collected yet */
+EOF
--
2.51.0
^ permalink raw reply related
* [PATCH v2 0/9] Kernel API Specification Framework
From: Sasha Levin @ 2026-03-22 12:10 UTC (permalink / raw)
To: linux-api, linux-kernel
Cc: linux-doc, linux-fsdevel, linux-kbuild, linux-kselftest,
workflows, tools, x86, Thomas Gleixner, Paul E . McKenney,
Greg Kroah-Hartman, Jonathan Corbet, Dmitry Vyukov, Randy Dunlap,
Cyril Hrubis, Kees Cook, Jake Edge, David Laight, Askar Safin,
Gabriele Paoloni, Mauro Carvalho Chehab, Christian Brauner,
Alexander Viro, Andrew Morton, Masahiro Yamada, Shuah Khan,
Ingo Molnar, Arnd Bergmann, Sasha Levin
This proposal introduces machinery for documenting kernel APIs, addressing the
long-standing challenge of maintaining stable interfaces between the kernel and
user-space programs. Despite the kernel's commitment to never breaking user
space, the lack of machine-readable API specifications has led to breakages and
across system calls and IOCTLs.
Specifications can document parameter types, valid ranges, constraints, and
alignment requirements. They capture return value semantics including success
conditions and error codes with their meaning. Execution context requirements,
capabilities, locking constraints, signal handling behavior, and side effects
can all be formally specified.
These specifications live alongside the code they document and are both
human-readable and machine-parseable. They can be validated at runtime when
CONFIG_KAPI_RUNTIME_CHECKS is enabled, exported via debugfs for userspace
tools, and extracted from either vmlinux or source code.
This enables static analysis tools to verify userspace API usage at compile
time, test generation based on formal specifications, consistent error handling
validation, automated documentation generation, and formal verification of
kernel interfaces.
The implementation includes a core framework with ELF section storage,
kerneldoc integration for inline specification, a debugfs interface for runtime
querying, and a Rust-based extraction tool (tools/kapi) supporting JSON, RST,
and plain text output formats. Example specifications are provided for the four
fundamental file syscalls (sys_open, sys_close, sys_read, sys_write). The
series also includes a KUnit test suite with 38 tests and a runtime
verification selftest with 29+ TAP tests.
The series with runtime testing enabled (CONFIG_KAPI_RUNTIME_CHECKS=y)
currently survives LTP tests in a KVM VM.
Changes since v1:
- Removed DEFINE_KERNEL_API_SPEC macro from user-facing documentation and
examples. The macros are now internal plumbing only; kerneldoc annotations
are the sole authoring interface. (Jonathan Corbet, Mauro Carvalho Chehab)
- Removed IOCTL specification section from documentation, as no IOCTL specs
are included in this series. (Jonathan Corbet)
- Removed since-version field entirely from the framework: struct
kernel_api_spec, debugfs output, JSON export, kdoc parser, and all
extractors/formatters in the kapi tool. (Jonathan Corbet)
- Removed stale :Date: field from documentation. (Jonathan Corbet)
- Fixed kmalloc documentation example to include both parameters (size and
flags) and a side-effect entry, matching the output example. (Jonathan Corbet)
- Simplified DSL references in documentation: converted Common Patterns
section from raw macros to kerneldoc annotation format, removed macro
references from Implementation Details and Troubleshooting. (Jonathan Corbet, Mauro Carvalho Chehab)
- Reworded sys_close spec to describe behavior without referencing kernel
internal callbacks: "flush callback" replaced with "close-time flush
operation", "release callback" references removed entirely. (Greg Kroah-Hartman)
- Removed HP-UX reference and "implementation-defined behavior" phrasing
from sys_close spec. (Greg Kroah-Hartman)
- Added copyright lines to all new files: C headers, kernel modules, Python
scripts, Rust source files, and selftests. (Greg Kroah-Hartman)
- Fixed rebasing artifact where patch 3 removed content from patch 1's
documentation additions. (Greg Kroah-Hartman)
- Removed unnecessary pr_info() from debugfs init and braces from
single-line for loop. (Greg Kroah-Hartman)
- Added commit message changelogs to all patches. (Greg Kroah-Hartman)
References:
v1: https://lore.kernel.org/all/20260313150928.2637368-1-sashal@kernel.org/
RFC v5: https://lore.kernel.org/lkml/20251218204239.4159453-1-sashal@kernel.org/
RFC v4: https://lore.kernel.org/lkml/20250825181434.3340805-1-sashal@kernel.org/
RFC v3: https://lore.kernel.org/lkml/20250711114248.2288591-1-sashal@kernel.org/
RFC v2: https://lore.kernel.org/lkml/20250624180742.5795-1-sashal@kernel.org/
RFC v1: https://lore.kernel.org/lkml/20250614134858.790460-1-sashal@kernel.org/
Sasha Levin (9):
kernel/api: introduce kernel API specification framework
kernel/api: enable kerneldoc-based API specifications
kernel/api: add debugfs interface for kernel API specifications
tools/kapi: Add kernel API specification extraction tool
kernel/api: add API specification for sys_open
kernel/api: add API specification for sys_close
kernel/api: add API specification for sys_read
kernel/api: add API specification for sys_write
kernel/api: add runtime verification selftest
.gitignore | 1 +
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kernel-api-spec.rst | 620 +++++++
MAINTAINERS | 12 +
arch/x86/include/asm/syscall_wrapper.h | 40 +
fs/open.c | 565 +++++-
fs/read_write.c | 683 +++++++
include/asm-generic/vmlinux.lds.h | 28 +
include/linux/kernel_api_spec.h | 1580 +++++++++++++++++
include/linux/syscall_api_spec.h | 186 ++
include/linux/syscalls.h | 39 +
init/Kconfig | 2 +
kernel/Makefile | 3 +
kernel/api/.gitignore | 2 +
kernel/api/Kconfig | 70 +
kernel/api/Makefile | 14 +
kernel/api/kapi_debugfs.c | 499 ++++++
kernel/api/kapi_kunit.c | 538 ++++++
kernel/api/kernel_api_spec.c | 1277 +++++++++++++
scripts/Makefile.build | 31 +
scripts/Makefile.clean | 3 +
tools/docs/kernel-doc | 5 +
tools/kapi/.gitignore | 4 +
tools/kapi/Cargo.toml | 19 +
tools/kapi/src/extractor/debugfs.rs | 578 ++++++
tools/kapi/src/extractor/kerneldoc_parser.rs | 1555 ++++++++++++++++
tools/kapi/src/extractor/mod.rs | 461 +++++
tools/kapi/src/extractor/source_parser.rs | 408 +++++
.../src/extractor/vmlinux/binary_utils.rs | 508 ++++++
.../src/extractor/vmlinux/magic_finder.rs | 115 ++
tools/kapi/src/extractor/vmlinux/mod.rs | 844 +++++++++
tools/kapi/src/formatter/json.rs | 720 ++++++++
tools/kapi/src/formatter/mod.rs | 142 ++
tools/kapi/src/formatter/plain.rs | 707 ++++++++
tools/kapi/src/formatter/rst.rs | 850 +++++++++
tools/kapi/src/main.rs | 122 ++
tools/lib/python/kdoc/kdoc_apispec.py | 888 +++++++++
tools/lib/python/kdoc/kdoc_output.py | 9 +-
tools/lib/python/kdoc/kdoc_parser.py | 85 +-
tools/testing/selftests/kapi/Makefile | 7 +
tools/testing/selftests/kapi/kapi_test_util.h | 33 +
tools/testing/selftests/kapi/test_kapi.c | 1023 +++++++++++
42 files changed, 15268 insertions(+), 9 deletions(-)
create mode 100644 Documentation/dev-tools/kernel-api-spec.rst
create mode 100644 include/linux/kernel_api_spec.h
create mode 100644 include/linux/syscall_api_spec.h
create mode 100644 kernel/api/.gitignore
create mode 100644 kernel/api/Kconfig
create mode 100644 kernel/api/Makefile
create mode 100644 kernel/api/kapi_debugfs.c
create mode 100644 kernel/api/kapi_kunit.c
create mode 100644 kernel/api/kernel_api_spec.c
create mode 100644 tools/kapi/.gitignore
create mode 100644 tools/kapi/Cargo.toml
create mode 100644 tools/kapi/src/extractor/debugfs.rs
create mode 100644 tools/kapi/src/extractor/kerneldoc_parser.rs
create mode 100644 tools/kapi/src/extractor/mod.rs
create mode 100644 tools/kapi/src/extractor/source_parser.rs
create mode 100644 tools/kapi/src/extractor/vmlinux/binary_utils.rs
create mode 100644 tools/kapi/src/extractor/vmlinux/magic_finder.rs
create mode 100644 tools/kapi/src/extractor/vmlinux/mod.rs
create mode 100644 tools/kapi/src/formatter/json.rs
create mode 100644 tools/kapi/src/formatter/mod.rs
create mode 100644 tools/kapi/src/formatter/plain.rs
create mode 100644 tools/kapi/src/formatter/rst.rs
create mode 100644 tools/kapi/src/main.rs
create mode 100644 tools/lib/python/kdoc/kdoc_apispec.py
create mode 100644 tools/testing/selftests/kapi/Makefile
create mode 100644 tools/testing/selftests/kapi/kapi_test_util.h
create mode 100644 tools/testing/selftests/kapi/test_kapi.c
--
2.51.0
^ permalink raw reply
* Re: [PATCH 1/9] kernel/api: introduce kernel API specification framework
From: Jonathan Corbet @ 2026-03-18 16:51 UTC (permalink / raw)
To: Sasha Levin
Cc: linux-api, linux-kernel, linux-doc, linux-fsdevel, linux-kbuild,
linux-kselftest, workflows, tools, x86, Thomas Gleixner,
Paul E. McKenney, Greg Kroah-Hartman, Dmitry Vyukov, Randy Dunlap,
Cyril Hrubis, Kees Cook, Jake Edge, David Laight, Askar Safin,
Gabriele Paoloni, Mauro Carvalho Chehab, Christian Brauner,
Alexander Viro, Andrew Morton, Masahiro Yamada, Shuah Khan,
Ingo Molnar, Arnd Bergmann
In-Reply-To: <abq3f5vdcwRXGJGX@laps>
Sasha Levin <sashal@kernel.org> writes:
> On Tue, Mar 17, 2026 at 11:49:27AM -0600, Jonathan Corbet wrote:
>>So the reason for two completely separate mechanisms is not entirely
>>clear to me. The kerneldoc variant is essentially documentation, while
>>the macro stuff is to be built into the executable? What if you want
>>both?
>>
>>It would be nice to only have one way if at all possible; I'm sure that
>>crossed your mind at some point :) If there have to be two, having both
>>examples describe the same function would make the parallels more clear.
>
> Woops, I forgot to finish writing my reply to this :)
>
> Under the hood, kerneldoc specs are translated into those macros so they could
> be part of the build process and embedded into the resulting binary (both for
> documentation as well as the runtime validation).
>
> I don't think anyone would use the macro format directly, but as it's there
> anyway I figured I'd offer it as an option. Would it make sense to just hide it
> behind the scenes?
If there are two ways of doing it, people will use both ways. My
kneejerk reaction would be to hide the macros as an implementation
detail, but perhaps that's just me.
Thanks,
jon
^ permalink raw reply
* Re: [PATCH 1/9] kernel/api: introduce kernel API specification framework
From: Jonathan Corbet @ 2026-03-18 16:50 UTC (permalink / raw)
To: Sasha Levin
Cc: linux-api, linux-kernel, linux-doc, linux-fsdevel, linux-kbuild,
linux-kselftest, workflows, tools, x86, Thomas Gleixner,
Paul E. McKenney, Greg Kroah-Hartman, Dmitry Vyukov, Randy Dunlap,
Cyril Hrubis, Kees Cook, Jake Edge, David Laight, Askar Safin,
Gabriele Paoloni, Mauro Carvalho Chehab, Christian Brauner,
Alexander Viro, Andrew Morton, Masahiro Yamada, Shuah Khan,
Ingo Molnar, Arnd Bergmann
In-Reply-To: <abq29NMpyXJrqv_7@laps>
Sasha Levin <sashal@kernel.org> writes:
>>> +The framework exports specifications via debugfs that can be used
>>> +to generate documentation. Tools for automatic documentation generation
>>> +from specifications are planned for future development.
>>
>>Documentation always comes last :)
>
> So this is one thing I wanted to run by the doc maintainers: the kapi tool
> already has the capability to export these specs in .rst format. Would it be
> interesting to have a manual for kenrel APIs in Documentation/? It can be
> automatically generated and updated and would always match the kernel code at
> that point.
I suspect we could certainly find a place for it. Maybe as a subsection
of the user-space API manual, at least for the system-call portion of
it.
Thanks,
jon
^ permalink raw reply
* Re: [PATCH 1/9] kernel/api: introduce kernel API specification framework
From: Sasha Levin @ 2026-03-18 14:53 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: Jonathan Corbet, linux-api, linux-kernel, linux-doc,
linux-fsdevel, linux-kbuild, linux-kselftest, workflows, tools,
x86, Thomas Gleixner, Paul E. McKenney, Greg Kroah-Hartman,
Dmitry Vyukov, Randy Dunlap, Cyril Hrubis, Kees Cook, Jake Edge,
David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann
In-Reply-To: <20260318070055.39f1af80@foz.lan>
On Wed, Mar 18, 2026 at 07:00:55AM +0100, Mauro Carvalho Chehab wrote:
>On Tue, 17 Mar 2026 11:49:27 -0600
>Jonathan Corbet <corbet@lwn.net> wrote:
>> So the reason for two completely separate mechanisms is not entirely
>> clear to me. The kerneldoc variant is essentially documentation, while
>> the macro stuff is to be built into the executable? What if you want
>> both?
>
>You can easily add support at kernel-doc to output such macros.
>
>All you need is to create a new class derived from OutputFormat and
>make it produce any different output format, including:
>
> #include <linux/kernel_api_spec.h>
>
> DEFINE_KERNEL_API_SPEC(sys_open)
> KAPI_DESCRIPTION("Open or create a file")
> KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
> /* ... parameter, error, constraint definitions ... */
> KAPI_END_SPEC
>
>I'd say that converting from such output to `.kapi_specs`` ELF section
>itself and/or to sysfs/debugfs - e.g. something that would require to
>compile or be linked with Kernel's compiled binaries should be done by a
>separate tool, but we should aim to have a singe tool to process
>kernel documentation markups.
>
>It is hard enough to maintain just one tool - and to have people actually
>writing documentation. Having a second one to handle it, with a different
>format will likely increase a lot the documentation burden.
So this is exactly what happens under the hood :) kerneldoc outputs these
macros and they get compiled ito the binary.
I exposed the macros as an option since they're there anyway, but I'm happy to
hide them as internal plumbing too.
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH 1/9] kernel/api: introduce kernel API specification framework
From: Sasha Levin @ 2026-03-18 14:32 UTC (permalink / raw)
To: Jonathan Corbet
Cc: linux-api, linux-kernel, linux-doc, linux-fsdevel, linux-kbuild,
linux-kselftest, workflows, tools, x86, Thomas Gleixner,
Paul E. McKenney, Greg Kroah-Hartman, Dmitry Vyukov, Randy Dunlap,
Cyril Hrubis, Kees Cook, Jake Edge, David Laight, Askar Safin,
Gabriele Paoloni, Mauro Carvalho Chehab, Christian Brauner,
Alexander Viro, Andrew Morton, Masahiro Yamada, Shuah Khan,
Ingo Molnar, Arnd Bergmann
In-Reply-To: <87h5qe9wig.fsf@trenco.lwn.net>
On Tue, Mar 17, 2026 at 11:49:27AM -0600, Jonathan Corbet wrote:
>So the reason for two completely separate mechanisms is not entirely
>clear to me. The kerneldoc variant is essentially documentation, while
>the macro stuff is to be built into the executable? What if you want
>both?
>
>It would be nice to only have one way if at all possible; I'm sure that
>crossed your mind at some point :) If there have to be two, having both
>examples describe the same function would make the parallels more clear.
Woops, I forgot to finish writing my reply to this :)
Under the hood, kerneldoc specs are translated into those macros so they could
be part of the build process and embedded into the resulting binary (both for
documentation as well as the runtime validation).
I don't think anyone would use the macro format directly, but as it's there
anyway I figured I'd offer it as an option. Would it make sense to just hide it
behind the scenes?
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH 1/9] kernel/api: introduce kernel API specification framework
From: Sasha Levin @ 2026-03-18 14:30 UTC (permalink / raw)
To: Jonathan Corbet
Cc: linux-api, linux-kernel, linux-doc, linux-fsdevel, linux-kbuild,
linux-kselftest, workflows, tools, x86, Thomas Gleixner,
Paul E. McKenney, Greg Kroah-Hartman, Dmitry Vyukov, Randy Dunlap,
Cyril Hrubis, Kees Cook, Jake Edge, David Laight, Askar Safin,
Gabriele Paoloni, Mauro Carvalho Chehab, Christian Brauner,
Alexander Viro, Andrew Morton, Masahiro Yamada, Shuah Khan,
Ingo Molnar, Arnd Bergmann
In-Reply-To: <87h5qe9wig.fsf@trenco.lwn.net>
On Tue, Mar 17, 2026 at 11:49:27AM -0600, Jonathan Corbet wrote:
>Sasha Levin <sashal@kernel.org> writes:
>
>> Add a framework for formally documenting kernel APIs with inline
>> specifications. This framework provides:
>>
>> - Structured API documentation with parameter specifications, return
>> values, error conditions, and execution context requirements
>> - Runtime validation capabilities for debugging (CONFIG_KAPI_RUNTIME_CHECKS)
>> - Export of specifications via debugfs for tooling integration
>> - Support for both internal kernel APIs and system calls
>
>So I'll confess I have only scanned over the implementation, but I have
>some thoughts on the earlier stuff.
>
>[...]
>
>> diff --git a/Documentation/dev-tools/kernel-api-spec.rst b/Documentation/dev-tools/kernel-api-spec.rst
>> new file mode 100644
>> index 0000000000000..7c0c1694f1f4a
>> --- /dev/null
>> +++ b/Documentation/dev-tools/kernel-api-spec.rst
>> @@ -0,0 +1,482 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +======================================
>> +Kernel API Specification Framework
>> +======================================
>> +
>> +:Author: Sasha Levin <sashal@kernel.org>
>> +:Date: June 2025
>
>Has it not changed since then?
>
>> +.. contents:: Table of Contents
>> + :depth: 3
>> + :local:
>> +
>> +Introduction
>> +============
>
>[...]
>
>> +Usage Guide
>> +===========
>> +
>> +Basic API Specification
>> +-----------------------
>> +
>> +API specifications are written as KAPI-annotated kerneldoc comments directly in
>> +the source file, immediately preceding the function implementation. The ``kapi``
>> +tool extracts these annotations to produce structured specifications.
>> +
>> +.. code-block:: c
>> +
>> + /**
>> + * kmalloc - allocate kernel memory
>> + * @size: Number of bytes to allocate
>> + * @flags: Allocation flags (GFP_*)
>
>Given that the text thus far has talked about user-space API validation,
>it's a bit surprising to see an internal function used as an example.
Indeed :)
I was playing with adding specs to lower level functions as a way to both
simplify the higher level spec blocks, but also to help validate the
correctness of those upper level specs ("you said that this syscall doesn't
allocate memory, but it's calling kmalloc()!").
>Also, maybe it should be kmalloc_obj()? <runs away>
>
>> + * context-flags: KAPI_CTX_PROCESS | KAPI_CTX_SOFTIRQ | KAPI_CTX_HARDIRQ
>> + * param-count: 2
>
>param-count is two, but you only document one of them?
>
>> + * param: size
>> + * type: KAPI_TYPE_UINT
>> + * flags: KAPI_PARAM_IN
>> + * constraint-type: KAPI_CONSTRAINT_RANGE
>> + * range: 0, KMALLOC_MAX_SIZE
>> + *
>> + * error: ENOMEM, Out of memory
>> + * desc: Insufficient memory available for the requested allocation.
>> + */
>
>Honest question: can this be made a bit easier for people to create,
>with less shift-key wear? My biggest worry with a system like this is
>that people won't take the time to create and maintain the entries, so
>anything that would ease the task would help. Is there an impediment to
>something like:
Before answering your actual question, I honestly think that most of these will
be LLM generated, so at least the skeleton of the kernel-doc will be there and
humans will only end up modifying the proposed spec.
> contexts: process, softirq, hardirq
>
> param: size
> type: uint, input
> constraint: range(0, KMALLOC_MAX_SIZE)
>
>See what I'm getting at? ISTM that your DSL could be made a bit less
>verbose and shouty while being just as well defined, but perhaps I'm
>missing something?
Yup, let me look into that.
>Even better, of course, would be to add a "description" field for each
>parameter, and allow that rather than the @param description that
>kerneldoc currently uses. That would keep all the information together,
>at the minor cost of adding another significant complication to the
>kernel-doc script. Mauro won't mind :)
>
>> + void *kmalloc(size_t size, gfp_t flags)
>> + {
>> + /* Implementation */
>> + }
>> +
>> +Alternatively, specifications can be defined using the ``DEFINE_KERNEL_API_SPEC``
>> +macro for compiled-in specs that are stored in the ``.kapi_specs`` ELF section:
>> +
>> +.. code-block:: c
>> +
>> + #include <linux/kernel_api_spec.h>
>> +
>> + DEFINE_KERNEL_API_SPEC(sys_open)
>> + KAPI_DESCRIPTION("Open or create a file")
>> + KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
>> + /* ... parameter, error, constraint definitions ... */
>> + KAPI_END_SPEC
>
>So the reason for two completely separate mechanisms is not entirely
>clear to me. The kerneldoc variant is essentially documentation, while
>the macro stuff is to be built into the executable? What if you want
>both?
>
>It would be nice to only have one way if at all possible; I'm sure that
>crossed your mind at some point :) If there have to be two, having both
>examples describe the same function would make the parallels more clear.
Under the hood, the
>> +System Call Specification
>> +-------------------------
>> +
>> +System calls are documented inline in the implementation file (e.g., ``fs/open.c``)
>> +using KAPI-annotated kerneldoc comments. When ``CONFIG_KAPI_RUNTIME_CHECKS`` is
>> +enabled, the ``SYSCALL_DEFINEx`` macros automatically look up the specification
>> +and validate parameters before and after the syscall executes.
>> +
>> +IOCTL Specification
>> +-------------------
>> +
>> +IOCTLs use the same annotation approach with additional structure field
>> +specifications
>
>This might be a really good place for an example
I think I'll just drop the IOCTL code for now. I used it in the RFCs to
demonstrate how a similar mechanism could be used throughout different
userspace API, but since I'm not using it in this series it probably makes
sense not to even talk about it.
>[...]
>
>> +Usage Examples
>> +--------------
>> +
>> +Query specific API::
>> +
>> + $ cat /sys/kernel/debug/kapi/apis/kmalloc/specification
>> + API: kmalloc
>> + Version: 3.0
>> + Description: Allocate kernel memory
>> +
>> + Parameters:
>> + [0] size (size_t, in): Number of bytes to allocate
>> + Range: 0 - 4194304
>> + [1] flags (flags, in): Allocation flags (GFP_*)
>> + Mask: 0x1ffffff
>
>Ah, you do document that second parameter somewhere :)
Apparently. I'll update the prior example. Not sure why it's missing :)
>> + Returns: pointer - Pointer to allocated memory or NULL
>> +
>> + Errors:
>> + ENOMEM: Out of memory
>> +
>> + Context: process, softirq, hardirq
>> +
>> + Side Effects:
>> + - Allocates memory from kernel heap
>
>That part wasn't in your example
>
>> +Export all specifications::
>> +
>> + $ cat /sys/kernel/debug/kapi/export/all.json > kernel-apis.json
>> +
>> +Enable validation for specific API::
>> +
>> + $ echo 1 > /sys/kernel/debug/kapi/apis/kmalloc/validate
>> +
>> +Performance Considerations
>> +==========================
>> +
>> +Memory Overhead
>> +---------------
>> +
>> +Each API specification consumes approximately 400-450KB of memory due to the
>> +fixed-size arrays in ``struct kernel_api_spec``. With the current 4 syscall
>> +specifications, total memory usage is approximately 1.7MB. Consider:
>
>Ouch.
It's pretty "dumb" at this point: large static arrays and such, mostly to keep
the code simple to read. If this is a concern for accepting this series, I'm
happy to shrink it down (by a lot).
>> +Documentation Generation
>> +------------------------
>> +
>> +The framework exports specifications via debugfs that can be used
>> +to generate documentation. Tools for automatic documentation generation
>> +from specifications are planned for future development.
>
>Documentation always comes last :)
So this is one thing I wanted to run by the doc maintainers: the kapi tool
already has the capability to export these specs in .rst format. Would it be
interesting to have a manual for kenrel APIs in Documentation/? It can be
automatically generated and updated and would always match the kernel code at
that point.
>Interesting stuff.
Thanks!
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH 5/9] kernel/api: add API specification for sys_open
From: Jonathan Corbet @ 2026-03-18 14:16 UTC (permalink / raw)
To: Sasha Levin
Cc: Greg Kroah-Hartman, linux-api, linux-kernel, linux-doc,
linux-fsdevel, linux-kbuild, linux-kselftest, workflows, tools,
x86, Thomas Gleixner, Paul E. McKenney, Dmitry Vyukov,
Randy Dunlap, Cyril Hrubis, Kees Cook, Jake Edge, David Laight,
Askar Safin, Gabriele Paoloni, Mauro Carvalho Chehab,
Christian Brauner, Alexander Viro, Andrew Morton, Masahiro Yamada,
Shuah Khan, Ingo Molnar, Arnd Bergmann
In-Reply-To: <abqyv0DiT0LFUU4v@laps>
Sasha Levin <sashal@kernel.org> writes:
> On Tue, Mar 17, 2026 at 12:37:13PM -0600, Jonathan Corbet wrote:
>>Sasha Levin <sashal@kernel.org> writes:
>>
>>> On Fri, Mar 13, 2026 at 04:33:57PM +0100, Greg Kroah-Hartman wrote:
>>>>On Fri, Mar 13, 2026 at 11:09:15AM -0400, Sasha Levin wrote:
>>
>>>>> + * since-version: 1.0
>>>>
>>>>I think since older versions :)
>>>
>>> Right. I guess that in my mind 1.0 was the first official "release". I'll
>>> update it to 0.01.
>>
>>That kind of raises the question of just what since-version means. The
>>version-0.01 (or 1.0) version of open() surely didn't do everything
>>described in this specification. So it's saying that some version of
>>the system call has existed since then?
>
> You know, I'm not entierly sure what the definition should be here.
>
> I stole this from man pages where they indicate at which version the API was
> introduced in. I'm not sure if it really adds any value.
>
> Any objections to just dropping it?
Not here. My general objective with the docs is to describe the kernel
as it is now, without that sort of history unless there's something
specific it can tell current users.
Thanks,
jon
^ permalink raw reply
* Re: [PATCH 0/9] Kernel API Specification Framework
From: Sasha Levin @ 2026-03-18 14:14 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: Jakub Kicinski, linux-api, linux-kernel, linux-doc, linux-fsdevel,
linux-kbuild, linux-kselftest, workflows, tools, x86,
Thomas Gleixner, Paul E. McKenney, Greg Kroah-Hartman,
Jonathan Corbet, Dmitry Vyukov, Randy Dunlap, Cyril Hrubis,
Kees Cook, Jake Edge, David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann
In-Reply-To: <20260318072410.72618011@foz.lan>
On Wed, Mar 18, 2026 at 07:24:10AM +0100, Mauro Carvalho Chehab wrote:
>On Sun, 15 Mar 2026 02:36:51 -0400
>Sasha Levin <sashal@kernel.org> wrote:
>
>> On Sat, Mar 14, 2026 at 11:18:22AM -0700, Jakub Kicinski wrote:
>> >On Fri, 13 Mar 2026 11:09:10 -0400 Sasha Levin wrote:
>> >> This enables static analysis tools to verify userspace API usage at compile
>> >> time, test generation based on formal specifications, consistent error handling
>> >> validation, automated documentation generation, and formal verification of
>> >> kernel interfaces.
>> >
>> >Could you give some examples? We have machine readable descriptions for
>> >Netlink interfaces, we approached syzbot folks and they did not really
>> >seem to care for those.
>>
>> Once the API is in a machine-readable format, we can write formatters to
>> output whatever downstream tools need.
>
>Kernel-doc already does that. The way it works is that it handles
>kernel-doc markups on two steps:
Cool, I'll take a look. We could throw away the source parser in the kapi tool
and just use the kerneldoc parse.
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH 5/9] kernel/api: add API specification for sys_open
From: Sasha Levin @ 2026-03-18 14:12 UTC (permalink / raw)
To: Jonathan Corbet
Cc: Greg Kroah-Hartman, linux-api, linux-kernel, linux-doc,
linux-fsdevel, linux-kbuild, linux-kselftest, workflows, tools,
x86, Thomas Gleixner, Paul E. McKenney, Dmitry Vyukov,
Randy Dunlap, Cyril Hrubis, Kees Cook, Jake Edge, David Laight,
Askar Safin, Gabriele Paoloni, Mauro Carvalho Chehab,
Christian Brauner, Alexander Viro, Andrew Morton, Masahiro Yamada,
Shuah Khan, Ingo Molnar, Arnd Bergmann
In-Reply-To: <878qbq9uau.fsf@trenco.lwn.net>
On Tue, Mar 17, 2026 at 12:37:13PM -0600, Jonathan Corbet wrote:
>Sasha Levin <sashal@kernel.org> writes:
>
>> On Fri, Mar 13, 2026 at 04:33:57PM +0100, Greg Kroah-Hartman wrote:
>>>On Fri, Mar 13, 2026 at 11:09:15AM -0400, Sasha Levin wrote:
>
>>>> + * since-version: 1.0
>>>
>>>I think since older versions :)
>>
>> Right. I guess that in my mind 1.0 was the first official "release". I'll
>> update it to 0.01.
>
>That kind of raises the question of just what since-version means. The
>version-0.01 (or 1.0) version of open() surely didn't do everything
>described in this specification. So it's saying that some version of
>the system call has existed since then?
You know, I'm not entierly sure what the definition should be here.
I stole this from man pages where they indicate at which version the API was
introduced in. I'm not sure if it really adds any value.
Any objections to just dropping it?
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH 0/9] Kernel API Specification Framework
From: Mauro Carvalho Chehab @ 2026-03-18 6:24 UTC (permalink / raw)
To: Sasha Levin
Cc: Jakub Kicinski, linux-api, linux-kernel, linux-doc, linux-fsdevel,
linux-kbuild, linux-kselftest, workflows, tools, x86,
Thomas Gleixner, Paul E. McKenney, Greg Kroah-Hartman,
Jonathan Corbet, Dmitry Vyukov, Randy Dunlap, Cyril Hrubis,
Kees Cook, Jake Edge, David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann
In-Reply-To: <abZTg9ZwnE5J4qXa@laps>
On Sun, 15 Mar 2026 02:36:51 -0400
Sasha Levin <sashal@kernel.org> wrote:
> On Sat, Mar 14, 2026 at 11:18:22AM -0700, Jakub Kicinski wrote:
> >On Fri, 13 Mar 2026 11:09:10 -0400 Sasha Levin wrote:
> >> This enables static analysis tools to verify userspace API usage at compile
> >> time, test generation based on formal specifications, consistent error handling
> >> validation, automated documentation generation, and formal verification of
> >> kernel interfaces.
> >
> >Could you give some examples? We have machine readable descriptions for
> >Netlink interfaces, we approached syzbot folks and they did not really
> >seem to care for those.
>
> Once the API is in a machine-readable format, we can write formatters to
> output whatever downstream tools need.
Kernel-doc already does that. The way it works is that it handles
kernel-doc markups on two steps:
- first step: parse kernel-doc markups, function prototypes and data
types for variables, typedefs, structs, unions, enums.
This is done inside tools/lib/python/kdoc/kdoc_parser.py.
The documentation is stored in memory as a list of documentation
entries. Each element there belongs to class KdocItem.
It is trivial to output its content in JSON or YAML format. I
submitted a path series a while ago doing exactly that, aiming to help
writing unittests for first step:
https://lore.kernel.org/linux-doc/7648cb5f5a1b501d9ae9a57b4d8dbeb7273d9097.1770128540.git.mchehab+huawei@kernel.org/
I'm planing to rebase such patch series on the top of my latest
kernel-doc patch series.
- second step: output generation. There is an abstract class named
OutputFormat which contains the following output methods:
def out_doc(self, fname, name, args):
"""Outputs a DOC block."""
def out_function(self, fname, name, args):
"""Outputs a function."""
def out_enum(self, fname, name, args):
"""Outputs an enum."""
def out_var(self, fname, name, args):
"""Outputs a variable."""
def out_typedef(self, fname, name, args):
"""Outputs a typedef."""
def out_struct(self, fname, name, args):
"""Outputs a struct."""
Producing a different output is as easy as doing:
class MyFormat(OutputFormat):
...
def out_var(self, fname, name, args):
self.data =+ f"whatever {name}"
...
Thanks,
Mauro
^ permalink raw reply
* Re: [PATCH 1/9] kernel/api: introduce kernel API specification framework
From: Mauro Carvalho Chehab @ 2026-03-18 6:00 UTC (permalink / raw)
To: Jonathan Corbet
Cc: Sasha Levin, linux-api, linux-kernel, linux-doc, linux-fsdevel,
linux-kbuild, linux-kselftest, workflows, tools, x86,
Thomas Gleixner, Paul E. McKenney, Greg Kroah-Hartman,
Dmitry Vyukov, Randy Dunlap, Cyril Hrubis, Kees Cook, Jake Edge,
David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann
In-Reply-To: <87h5qe9wig.fsf@trenco.lwn.net>
On Tue, 17 Mar 2026 11:49:27 -0600
Jonathan Corbet <corbet@lwn.net> wrote:
> Sasha Levin <sashal@kernel.org> writes:
>
> > Add a framework for formally documenting kernel APIs with inline
> > specifications. This framework provides:
> >
> > - Structured API documentation with parameter specifications, return
> > values, error conditions, and execution context requirements
> > - Runtime validation capabilities for debugging (CONFIG_KAPI_RUNTIME_CHECKS)
> > - Export of specifications via debugfs for tooling integration
> > - Support for both internal kernel APIs and system calls
>
> So I'll confess I have only scanned over the implementation, but I have
> some thoughts on the earlier stuff.
>
> [...]
>
> > diff --git a/Documentation/dev-tools/kernel-api-spec.rst b/Documentation/dev-tools/kernel-api-spec.rst
> > new file mode 100644
> > index 0000000000000..7c0c1694f1f4a
> > --- /dev/null
> > +++ b/Documentation/dev-tools/kernel-api-spec.rst
> > @@ -0,0 +1,482 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +======================================
> > +Kernel API Specification Framework
> > +======================================
> > +
> > +:Author: Sasha Levin <sashal@kernel.org>
> > +:Date: June 2025
>
> Has it not changed since then?
>
> > +.. contents:: Table of Contents
> > + :depth: 3
> > + :local:
> > +
> > +Introduction
> > +============
>
> [...]
>
> > +Usage Guide
> > +===========
> > +
> > +Basic API Specification
> > +-----------------------
> > +
> > +API specifications are written as KAPI-annotated kerneldoc comments directly in
> > +the source file, immediately preceding the function implementation. The ``kapi``
> > +tool extracts these annotations to produce structured specifications.
> > +
> > +.. code-block:: c
> > +
> > + /**
> > + * kmalloc - allocate kernel memory
> > + * @size: Number of bytes to allocate
> > + * @flags: Allocation flags (GFP_*)
>
> Given that the text thus far has talked about user-space API validation,
> it's a bit surprising to see an internal function used as an example.
>
> Also, maybe it should be kmalloc_obj()? <runs away>
>
> > + * context-flags: KAPI_CTX_PROCESS | KAPI_CTX_SOFTIRQ | KAPI_CTX_HARDIRQ
> > + * param-count: 2
>
> param-count is two, but you only document one of them?
>
> > + * param: size
> > + * type: KAPI_TYPE_UINT
> > + * flags: KAPI_PARAM_IN
> > + * constraint-type: KAPI_CONSTRAINT_RANGE
> > + * range: 0, KMALLOC_MAX_SIZE
> > + *
> > + * error: ENOMEM, Out of memory
> > + * desc: Insufficient memory available for the requested allocation.
> > + */
>
> Honest question: can this be made a bit easier for people to create,
> with less shift-key wear? My biggest worry with a system like this is
> that people won't take the time to create and maintain the entries, so
> anything that would ease the task would help. Is there an impediment to
> something like:
>
> contexts: process, softirq, hardirq
>
> param: size
> type: uint, input
> constraint: range(0, KMALLOC_MAX_SIZE)
>
> See what I'm getting at? ISTM that your DSL could be made a bit less
> verbose and shouty while being just as well defined, but perhaps I'm
> missing something?
>
> Even better, of course, would be to add a "description" field for each
> parameter, and allow that rather than the @param description that
> kerneldoc currently uses. That would keep all the information together,
> at the minor cost of adding another significant complication to the
> kernel-doc script. Mauro won't mind :)
No, I won't ;-)
It sounds a lot better to use kernel-doc also for kAPI than to have an
independent tool.
It is also very confusing if we end with a kernel-doc-like format
that it is not parsed by kernel-doc.
>
> > + void *kmalloc(size_t size, gfp_t flags)
> > + {
> > + /* Implementation */
> > + }
> > +
> > +Alternatively, specifications can be defined using the ``DEFINE_KERNEL_API_SPEC``
> > +macro for compiled-in specs that are stored in the ``.kapi_specs`` ELF section:
> > +
> > +.. code-block:: c
> > +
> > + #include <linux/kernel_api_spec.h>
> > +
> > + DEFINE_KERNEL_API_SPEC(sys_open)
> > + KAPI_DESCRIPTION("Open or create a file")
> > + KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
> > + /* ... parameter, error, constraint definitions ... */
> > + KAPI_END_SPEC
>
> So the reason for two completely separate mechanisms is not entirely
> clear to me. The kerneldoc variant is essentially documentation, while
> the macro stuff is to be built into the executable? What if you want
> both?
You can easily add support at kernel-doc to output such macros.
All you need is to create a new class derived from OutputFormat and
make it produce any different output format, including:
#include <linux/kernel_api_spec.h>
DEFINE_KERNEL_API_SPEC(sys_open)
KAPI_DESCRIPTION("Open or create a file")
KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
/* ... parameter, error, constraint definitions ... */
KAPI_END_SPEC
I'd say that converting from such output to `.kapi_specs`` ELF section
itself and/or to sysfs/debugfs - e.g. something that would require to
compile or be linked with Kernel's compiled binaries should be done by a
separate tool, but we should aim to have a singe tool to process
kernel documentation markups.
It is hard enough to maintain just one tool - and to have people actually
writing documentation. Having a second one to handle it, with a different
format will likely increase a lot the documentation burden.
> It would be nice to only have one way if at all possible; I'm sure that
> crossed your mind at some point :) If there have to be two, having both
> examples describe the same function would make the parallels more clear.
>
> > +System Call Specification
> > +-------------------------
> > +
> > +System calls are documented inline in the implementation file (e.g., ``fs/open.c``)
> > +using KAPI-annotated kerneldoc comments. When ``CONFIG_KAPI_RUNTIME_CHECKS`` is
> > +enabled, the ``SYSCALL_DEFINEx`` macros automatically look up the specification
> > +and validate parameters before and after the syscall executes.
> > +
> > +IOCTL Specification
> > +-------------------
> > +
> > +IOCTLs use the same annotation approach with additional structure field
> > +specifications
>
> This might be a really good place for an example
>
> [...]
>
> > +Usage Examples
> > +--------------
> > +
> > +Query specific API::
> > +
> > + $ cat /sys/kernel/debug/kapi/apis/kmalloc/specification
> > + API: kmalloc
> > + Version: 3.0
> > + Description: Allocate kernel memory
> > +
> > + Parameters:
> > + [0] size (size_t, in): Number of bytes to allocate
> > + Range: 0 - 4194304
> > + [1] flags (flags, in): Allocation flags (GFP_*)
> > + Mask: 0x1ffffff
>
> Ah, you do document that second parameter somewhere :)
>
> > + Returns: pointer - Pointer to allocated memory or NULL
> > +
> > + Errors:
> > + ENOMEM: Out of memory
> > +
> > + Context: process, softirq, hardirq
> > +
> > + Side Effects:
> > + - Allocates memory from kernel heap
>
> That part wasn't in your example
>
> > +Export all specifications::
> > +
> > + $ cat /sys/kernel/debug/kapi/export/all.json > kernel-apis.json
> > +
> > +Enable validation for specific API::
> > +
> > + $ echo 1 > /sys/kernel/debug/kapi/apis/kmalloc/validate
> > +
> > +Performance Considerations
> > +==========================
> > +
> > +Memory Overhead
> > +---------------
> > +
> > +Each API specification consumes approximately 400-450KB of memory due to the
> > +fixed-size arrays in ``struct kernel_api_spec``. With the current 4 syscall
> > +specifications, total memory usage is approximately 1.7MB. Consider:
>
> Ouch.
>
> > +Documentation Generation
> > +------------------------
> > +
> > +The framework exports specifications via debugfs that can be used
> > +to generate documentation. Tools for automatic documentation generation
> > +from specifications are planned for future development.
>
> Documentation always comes last :)
>
> Interesting stuff.
>
> jon
Thanks,
Mauro
^ permalink raw reply
* Re: [PATCH 5/9] kernel/api: add API specification for sys_open
From: Jonathan Corbet @ 2026-03-17 18:37 UTC (permalink / raw)
To: Sasha Levin, Greg Kroah-Hartman
Cc: linux-api, linux-kernel, linux-doc, linux-fsdevel, linux-kbuild,
linux-kselftest, workflows, tools, x86, Thomas Gleixner,
Paul E. McKenney, Dmitry Vyukov, Randy Dunlap, Cyril Hrubis,
Kees Cook, Jake Edge, David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann
In-Reply-To: <abQ-iIylzpuqlRv3@laps>
Sasha Levin <sashal@kernel.org> writes:
> On Fri, Mar 13, 2026 at 04:33:57PM +0100, Greg Kroah-Hartman wrote:
>>On Fri, Mar 13, 2026 at 11:09:15AM -0400, Sasha Levin wrote:
>>> + * since-version: 1.0
>>
>>I think since older versions :)
>
> Right. I guess that in my mind 1.0 was the first official "release". I'll
> update it to 0.01.
That kind of raises the question of just what since-version means. The
version-0.01 (or 1.0) version of open() surely didn't do everything
described in this specification. So it's saying that some version of
the system call has existed since then?
Thanks,
jon
^ permalink raw reply
* Re: [PATCH 1/9] kernel/api: introduce kernel API specification framework
From: Jonathan Corbet @ 2026-03-17 17:49 UTC (permalink / raw)
To: Sasha Levin, linux-api, linux-kernel
Cc: linux-doc, linux-fsdevel, linux-kbuild, linux-kselftest,
workflows, tools, x86, Thomas Gleixner, Paul E. McKenney,
Greg Kroah-Hartman, Dmitry Vyukov, Randy Dunlap, Cyril Hrubis,
Kees Cook, Jake Edge, David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann, Sasha Levin
In-Reply-To: <20260313150928.2637368-2-sashal@kernel.org>
Sasha Levin <sashal@kernel.org> writes:
> Add a framework for formally documenting kernel APIs with inline
> specifications. This framework provides:
>
> - Structured API documentation with parameter specifications, return
> values, error conditions, and execution context requirements
> - Runtime validation capabilities for debugging (CONFIG_KAPI_RUNTIME_CHECKS)
> - Export of specifications via debugfs for tooling integration
> - Support for both internal kernel APIs and system calls
So I'll confess I have only scanned over the implementation, but I have
some thoughts on the earlier stuff.
[...]
> diff --git a/Documentation/dev-tools/kernel-api-spec.rst b/Documentation/dev-tools/kernel-api-spec.rst
> new file mode 100644
> index 0000000000000..7c0c1694f1f4a
> --- /dev/null
> +++ b/Documentation/dev-tools/kernel-api-spec.rst
> @@ -0,0 +1,482 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +======================================
> +Kernel API Specification Framework
> +======================================
> +
> +:Author: Sasha Levin <sashal@kernel.org>
> +:Date: June 2025
Has it not changed since then?
> +.. contents:: Table of Contents
> + :depth: 3
> + :local:
> +
> +Introduction
> +============
[...]
> +Usage Guide
> +===========
> +
> +Basic API Specification
> +-----------------------
> +
> +API specifications are written as KAPI-annotated kerneldoc comments directly in
> +the source file, immediately preceding the function implementation. The ``kapi``
> +tool extracts these annotations to produce structured specifications.
> +
> +.. code-block:: c
> +
> + /**
> + * kmalloc - allocate kernel memory
> + * @size: Number of bytes to allocate
> + * @flags: Allocation flags (GFP_*)
Given that the text thus far has talked about user-space API validation,
it's a bit surprising to see an internal function used as an example.
Also, maybe it should be kmalloc_obj()? <runs away>
> + * context-flags: KAPI_CTX_PROCESS | KAPI_CTX_SOFTIRQ | KAPI_CTX_HARDIRQ
> + * param-count: 2
param-count is two, but you only document one of them?
> + * param: size
> + * type: KAPI_TYPE_UINT
> + * flags: KAPI_PARAM_IN
> + * constraint-type: KAPI_CONSTRAINT_RANGE
> + * range: 0, KMALLOC_MAX_SIZE
> + *
> + * error: ENOMEM, Out of memory
> + * desc: Insufficient memory available for the requested allocation.
> + */
Honest question: can this be made a bit easier for people to create,
with less shift-key wear? My biggest worry with a system like this is
that people won't take the time to create and maintain the entries, so
anything that would ease the task would help. Is there an impediment to
something like:
contexts: process, softirq, hardirq
param: size
type: uint, input
constraint: range(0, KMALLOC_MAX_SIZE)
See what I'm getting at? ISTM that your DSL could be made a bit less
verbose and shouty while being just as well defined, but perhaps I'm
missing something?
Even better, of course, would be to add a "description" field for each
parameter, and allow that rather than the @param description that
kerneldoc currently uses. That would keep all the information together,
at the minor cost of adding another significant complication to the
kernel-doc script. Mauro won't mind :)
> + void *kmalloc(size_t size, gfp_t flags)
> + {
> + /* Implementation */
> + }
> +
> +Alternatively, specifications can be defined using the ``DEFINE_KERNEL_API_SPEC``
> +macro for compiled-in specs that are stored in the ``.kapi_specs`` ELF section:
> +
> +.. code-block:: c
> +
> + #include <linux/kernel_api_spec.h>
> +
> + DEFINE_KERNEL_API_SPEC(sys_open)
> + KAPI_DESCRIPTION("Open or create a file")
> + KAPI_CONTEXT(KAPI_CTX_PROCESS | KAPI_CTX_SLEEPABLE)
> + /* ... parameter, error, constraint definitions ... */
> + KAPI_END_SPEC
So the reason for two completely separate mechanisms is not entirely
clear to me. The kerneldoc variant is essentially documentation, while
the macro stuff is to be built into the executable? What if you want
both?
It would be nice to only have one way if at all possible; I'm sure that
crossed your mind at some point :) If there have to be two, having both
examples describe the same function would make the parallels more clear.
> +System Call Specification
> +-------------------------
> +
> +System calls are documented inline in the implementation file (e.g., ``fs/open.c``)
> +using KAPI-annotated kerneldoc comments. When ``CONFIG_KAPI_RUNTIME_CHECKS`` is
> +enabled, the ``SYSCALL_DEFINEx`` macros automatically look up the specification
> +and validate parameters before and after the syscall executes.
> +
> +IOCTL Specification
> +-------------------
> +
> +IOCTLs use the same annotation approach with additional structure field
> +specifications
This might be a really good place for an example
[...]
> +Usage Examples
> +--------------
> +
> +Query specific API::
> +
> + $ cat /sys/kernel/debug/kapi/apis/kmalloc/specification
> + API: kmalloc
> + Version: 3.0
> + Description: Allocate kernel memory
> +
> + Parameters:
> + [0] size (size_t, in): Number of bytes to allocate
> + Range: 0 - 4194304
> + [1] flags (flags, in): Allocation flags (GFP_*)
> + Mask: 0x1ffffff
Ah, you do document that second parameter somewhere :)
> + Returns: pointer - Pointer to allocated memory or NULL
> +
> + Errors:
> + ENOMEM: Out of memory
> +
> + Context: process, softirq, hardirq
> +
> + Side Effects:
> + - Allocates memory from kernel heap
That part wasn't in your example
> +Export all specifications::
> +
> + $ cat /sys/kernel/debug/kapi/export/all.json > kernel-apis.json
> +
> +Enable validation for specific API::
> +
> + $ echo 1 > /sys/kernel/debug/kapi/apis/kmalloc/validate
> +
> +Performance Considerations
> +==========================
> +
> +Memory Overhead
> +---------------
> +
> +Each API specification consumes approximately 400-450KB of memory due to the
> +fixed-size arrays in ``struct kernel_api_spec``. With the current 4 syscall
> +specifications, total memory usage is approximately 1.7MB. Consider:
Ouch.
> +Documentation Generation
> +------------------------
> +
> +The framework exports specifications via debugfs that can be used
> +to generate documentation. Tools for automatic documentation generation
> +from specifications are planned for future development.
Documentation always comes last :)
Interesting stuff.
jon
^ permalink raw reply
* Re: [PATCH 0/9] Kernel API Specification Framework
From: Sasha Levin @ 2026-03-16 23:29 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Dmitry Vyukov, syzkaller, linux-api, linux-kernel, linux-doc,
linux-fsdevel, linux-kbuild, linux-kselftest, workflows, tools,
x86, Thomas Gleixner, Paul E. McKenney, Greg Kroah-Hartman,
Jonathan Corbet, Randy Dunlap, Cyril Hrubis, Kees Cook, Jake Edge,
David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann
In-Reply-To: <20260316155756.25b985f5@kernel.org>
On Mon, Mar 16, 2026 at 03:57:56PM -0700, Jakub Kicinski wrote:
>Didn't even think of that. LLMs should make short work of this sort of
>extraction of information from source code..
This is the primary reason that this proposal resurfaced :)
I've originally proposed[1] something like this almost a decade ago, but when I
started trying to write the actual specs I hit a brick wall: it was simply not
tractable.
With LLMs, writing the specs is something we can actually pull off, and we can
verify their correctness so LLMs don't get to hallucinate.
The specs you see in the following patches are all LLM generated.
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH 0/9] Kernel API Specification Framework
From: Jakub Kicinski @ 2026-03-16 22:57 UTC (permalink / raw)
To: Dmitry Vyukov
Cc: syzkaller, Sasha Levin, linux-api, linux-kernel, linux-doc,
linux-fsdevel, linux-kbuild, linux-kselftest, workflows, tools,
x86, Thomas Gleixner, Paul E. McKenney, Greg Kroah-Hartman,
Jonathan Corbet, Randy Dunlap, Cyril Hrubis, Kees Cook, Jake Edge,
David Laight, Askar Safin, Gabriele Paoloni,
Mauro Carvalho Chehab, Christian Brauner, Alexander Viro,
Andrew Morton, Masahiro Yamada, Shuah Khan, Ingo Molnar,
Arnd Bergmann
In-Reply-To: <CACT4Y+arWePyxnV3hWk5RanWZpoc7=ALQ6DV_2MCuQkNoTtJUw@mail.gmail.com>
On Mon, 16 Mar 2026 08:05:53 +0100 Dmitry Vyukov wrote:
> On Sat, 14 Mar 2026 at 19:18, Jakub Kicinski <kuba@kernel.org> wrote:
> > On Fri, 13 Mar 2026 11:09:10 -0400 Sasha Levin wrote:
> > > This enables static analysis tools to verify userspace API usage at compile
> > > time, test generation based on formal specifications, consistent error handling
> > > validation, automated documentation generation, and formal verification of
> > > kernel interfaces.
> >
> > Could you give some examples? We have machine readable descriptions for
> > Netlink interfaces, we approached syzbot folks and they did not really
> > seem to care for those.
>
> I think our reasoning wrt syzkaller was that not all interfaces in all
> relevant kernels are described with netlink yml descriptions, so we
> need to continue using the extraction of interfaces from the source
> code. And if we have that code, then using yml as an additional data
> source only adds code/complexity. Additionally, we may extract some
> extra constraints/info from code that are not present in yml.
>
> Realistically system call descriptions may have the same problem for
> us at this point, since we extract lots of info from the source code
> already:
> https://raw.githubusercontent.com/google/syzkaller/refs/heads/master/sys/linux/auto.txt
yup! we haven't tried to make the yml spec super useful to syzbot
to be fair. I'm just flagging that example because in our case we
quickly went from "this will obviously be useful to syzbot" to
"although we could, it may not be super practical"
> (and LLMs obviously can allow us to extract more)
Didn't even think of that. LLMs should make short work of this sort of
extraction of information from source code..
^ permalink raw reply
* Re: [PATCH net-next v2 00/14] tcp: preserve receive-window accounting across ratio drift
From: Wesley Atwell @ 2026-03-16 18:03 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, davem, kuba, edumazet, ncardwell, linux-kernel, linux-api,
linux-doc, linux-kselftest, linux-trace-kernel, mptcp, dsahern,
horms, kuniyu, andrew+netdev, willemdebruijn.kernel, jasowang,
skhan, corbet, matttbe, martineau, geliang, rostedt, mhiramat,
mathieu.desnoyers, 0x7f454c46
In-Reply-To: <e93ce797-4153-4e6e-89b6-3839a0b8bca2@redhat.com>
Hi Paolo,
The link I meant is the netdev regression discussion that led to:
026dfef287c0 ("tcp: give up on stronger sk_rcvbuf checks (for now)")
The report/discussion thread is here:
https://lore.kernel.org/20260225122355.585fd57b@kernel.org
The revert posting itself is here:
https://patch.msgid.link/20260227003359.2391017-1-kuba@kernel.org
I should not have implied that there was a separate prior
regression report beyond that thread.
Thanks for the note on formatting. I will keep follow-ups in plaintext
and avoid top-posting.
Thanks,
Wesley
^ permalink raw reply
* Re: [PATCH net-next v2 00/14] tcp: preserve receive-window accounting across ratio drift
From: Paolo Abeni @ 2026-03-16 17:47 UTC (permalink / raw)
To: Wesley Atwell
Cc: netdev, davem, kuba, edumazet, ncardwell, linux-kernel, linux-api,
linux-doc, linux-kselftest, linux-trace-kernel, mptcp, dsahern,
horms, kuniyu, andrew+netdev, willemdebruijn.kernel, jasowang,
skhan, corbet, matttbe, martineau, geliang, rostedt, mhiramat,
mathieu.desnoyers, 0x7f454c46
In-Reply-To: <CAN=sVvyNpkyok_bt8eQSmqc4f7g7QoZBUmRmNRLoFz1HasEzMA@mail.gmail.com>
Hi,
On 3/16/26 6:29 PM, Wesley Atwell wrote:
> The strongest real anchor here is the already documented regression
> around sender-visible rwnd diverging from hard receive-memory backing,
> rather than a general receive-accounting cleanup.
I likely missed some of the prior discussion. Could you please share a
pointer/link to the mentioned regression report?
When posting on netdev please:
- use plaintext only messages
- avoid top-posting
Thanks,
Paolo
^ permalink raw reply
* Re: [PATCH v5 1/4] openat2: new OPENAT2_REGULAR flag support
From: Dorjoy Chowdhury @ 2026-03-16 17:22 UTC (permalink / raw)
To: Jeff Layton
Cc: linux-fsdevel, linux-kernel, linux-api, ceph-devel, gfs2,
linux-nfs, linux-cifs, v9fs, linux-kselftest, viro, brauner, jack,
chuck.lever, alex.aring, arnd, adilger, mjguzik, smfrench,
richard.henderson, mattst88, linmag7, tsbogend, James.Bottomley,
deller, davem, andreas, idryomov, amarkuze, slava, agruenba,
trondmy, anna, sfrench, pc, ronniesahlberg, sprasad, tom,
bharathsm, shuah, miklos, hansg
In-Reply-To: <5fcc2a6e6d92dae0601c6b3b8faa8b2f83981afb.camel@kernel.org>
On Mon, Mar 16, 2026 at 10:53 PM Jeff Layton <jlayton@kernel.org> wrote:
>
> On Sat, 2026-03-07 at 20:06 +0600, Dorjoy Chowdhury wrote:
> > This flag indicates the path should be opened if it's a regular file.
> > This is useful to write secure programs that want to avoid being
> > tricked into opening device nodes with special semantics while thinking
> > they operate on regular files. This is a requested feature from the
> > uapi-group[1].
> >
> > A corresponding error code EFTYPE has been introduced. For example, if
> > openat2 is called on path /dev/null with OPENAT2_REGULAR in the flag
> > param, it will return -EFTYPE. EFTYPE is already used in BSD systems
> > like FreeBSD, macOS.
> >
> > When used in combination with O_CREAT, either the regular file is
> > created, or if the path already exists, it is opened if it's a regular
> > file. Otherwise, -EFTYPE is returned.
> >
> > When OPENAT2_REGULAR is combined with O_DIRECTORY, -EINVAL is returned
> > as it doesn't make sense to open a path that is both a directory and a
> > regular file.
> >
> > [1]: https://uapi-group.org/kernel-features/#ability-to-only-open-regular-files
> >
> > Signed-off-by: Dorjoy Chowdhury <dorjoychy111@gmail.com>
> > ---
> > arch/alpha/include/uapi/asm/errno.h | 2 ++
> > arch/alpha/include/uapi/asm/fcntl.h | 1 +
> > arch/mips/include/uapi/asm/errno.h | 2 ++
> > arch/parisc/include/uapi/asm/errno.h | 2 ++
> > arch/parisc/include/uapi/asm/fcntl.h | 1 +
> > arch/sparc/include/uapi/asm/errno.h | 2 ++
> > arch/sparc/include/uapi/asm/fcntl.h | 1 +
> > fs/ceph/file.c | 4 ++++
> > fs/gfs2/inode.c | 6 ++++++
> > fs/namei.c | 4 ++++
> > fs/nfs/dir.c | 4 ++++
> > fs/open.c | 4 +++-
> > fs/smb/client/dir.c | 14 +++++++++++++-
> > include/linux/fcntl.h | 2 ++
> > include/uapi/asm-generic/errno.h | 2 ++
> > include/uapi/asm-generic/fcntl.h | 4 ++++
> > tools/arch/alpha/include/uapi/asm/errno.h | 2 ++
> > tools/arch/mips/include/uapi/asm/errno.h | 2 ++
> > tools/arch/parisc/include/uapi/asm/errno.h | 2 ++
> > tools/arch/sparc/include/uapi/asm/errno.h | 2 ++
> > tools/include/uapi/asm-generic/errno.h | 2 ++
> > 21 files changed, 63 insertions(+), 2 deletions(-)
> >
> >
>
> I pointed Claude at this patch and got this back. Both issues that it
> found will need to be fixed:
>
> Analysis Summary
>
> Commit: 7e7fa2653ca57 - openat2: new OPENAT2_REGULAR flag support
>
> This patch adds a new OPENAT2_REGULAR flag for openat2() that restricts opens to regular files only, returning a new
> EFTYPE errno for non-regular files. It adds filesystem-specific checks in ceph, gfs2, nfs, and cifs atomic_open paths,
> plus a VFS-level fallback in do_open().
>
> Issues found:
>
> 1. OPENAT2_REGULAR leaks into f_flags - do_dentry_open() strips open-time-only flags (O_CREAT|O_EXCL|O_NOCTTY|O_TRUNC)
> but does not strip OPENAT2_REGULAR. When a regular file is successfully opened via openat2() with this flag, the bit
> persists in file->f_flags and will be returned by fcntl(fd, F_GETFL).
> 2. BUILD_BUG_ON not updated - The compile-time guard checks upper_32_bits(VALID_OPEN_FLAGS) but the code now accepts
> VALID_OPENAT2_FLAGS. The guard should cover the expanded flag set.
>
Good catches! I guess for issue 1 I need to modify the line in
do_dentry_open implementation to
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | OPENAT2_REGULAR);
right?
And for issue 2, I should change the VALID_OPEN_FLAGS to
VALID_OPENAT2_FLAGS in both build_open_flags in fs/open.c and in
fcntl_init in fs/fcntl.c, correct?
Regards,
Dorjoy
^ permalink raw reply
* Re: [PATCH v5 1/4] openat2: new OPENAT2_REGULAR flag support
From: Jeff Layton @ 2026-03-16 16:53 UTC (permalink / raw)
To: Dorjoy Chowdhury, linux-fsdevel
Cc: linux-kernel, linux-api, ceph-devel, gfs2, linux-nfs, linux-cifs,
v9fs, linux-kselftest, viro, brauner, jack, chuck.lever,
alex.aring, arnd, adilger, mjguzik, smfrench, richard.henderson,
mattst88, linmag7, tsbogend, James.Bottomley, deller, davem,
andreas, idryomov, amarkuze, slava, agruenba, trondmy, anna,
sfrench, pc, ronniesahlberg, sprasad, tom, bharathsm, shuah,
miklos, hansg
In-Reply-To: <20260307140726.70219-2-dorjoychy111@gmail.com>
On Sat, 2026-03-07 at 20:06 +0600, Dorjoy Chowdhury wrote:
> This flag indicates the path should be opened if it's a regular file.
> This is useful to write secure programs that want to avoid being
> tricked into opening device nodes with special semantics while thinking
> they operate on regular files. This is a requested feature from the
> uapi-group[1].
>
> A corresponding error code EFTYPE has been introduced. For example, if
> openat2 is called on path /dev/null with OPENAT2_REGULAR in the flag
> param, it will return -EFTYPE. EFTYPE is already used in BSD systems
> like FreeBSD, macOS.
>
> When used in combination with O_CREAT, either the regular file is
> created, or if the path already exists, it is opened if it's a regular
> file. Otherwise, -EFTYPE is returned.
>
> When OPENAT2_REGULAR is combined with O_DIRECTORY, -EINVAL is returned
> as it doesn't make sense to open a path that is both a directory and a
> regular file.
>
> [1]: https://uapi-group.org/kernel-features/#ability-to-only-open-regular-files
>
> Signed-off-by: Dorjoy Chowdhury <dorjoychy111@gmail.com>
> ---
> arch/alpha/include/uapi/asm/errno.h | 2 ++
> arch/alpha/include/uapi/asm/fcntl.h | 1 +
> arch/mips/include/uapi/asm/errno.h | 2 ++
> arch/parisc/include/uapi/asm/errno.h | 2 ++
> arch/parisc/include/uapi/asm/fcntl.h | 1 +
> arch/sparc/include/uapi/asm/errno.h | 2 ++
> arch/sparc/include/uapi/asm/fcntl.h | 1 +
> fs/ceph/file.c | 4 ++++
> fs/gfs2/inode.c | 6 ++++++
> fs/namei.c | 4 ++++
> fs/nfs/dir.c | 4 ++++
> fs/open.c | 4 +++-
> fs/smb/client/dir.c | 14 +++++++++++++-
> include/linux/fcntl.h | 2 ++
> include/uapi/asm-generic/errno.h | 2 ++
> include/uapi/asm-generic/fcntl.h | 4 ++++
> tools/arch/alpha/include/uapi/asm/errno.h | 2 ++
> tools/arch/mips/include/uapi/asm/errno.h | 2 ++
> tools/arch/parisc/include/uapi/asm/errno.h | 2 ++
> tools/arch/sparc/include/uapi/asm/errno.h | 2 ++
> tools/include/uapi/asm-generic/errno.h | 2 ++
> 21 files changed, 63 insertions(+), 2 deletions(-)
>
>
I pointed Claude at this patch and got this back. Both issues that it
found will need to be fixed:
Analysis Summary
Commit: 7e7fa2653ca57 - openat2: new OPENAT2_REGULAR flag support
This patch adds a new OPENAT2_REGULAR flag for openat2() that restricts opens to regular files only, returning a new
EFTYPE errno for non-regular files. It adds filesystem-specific checks in ceph, gfs2, nfs, and cifs atomic_open paths,
plus a VFS-level fallback in do_open().
Issues found:
1. OPENAT2_REGULAR leaks into f_flags - do_dentry_open() strips open-time-only flags (O_CREAT|O_EXCL|O_NOCTTY|O_TRUNC)
but does not strip OPENAT2_REGULAR. When a regular file is successfully opened via openat2() with this flag, the bit
persists in file->f_flags and will be returned by fcntl(fd, F_GETFL).
2. BUILD_BUG_ON not updated - The compile-time guard checks upper_32_bits(VALID_OPEN_FLAGS) but the code now accepts
VALID_OPENAT2_FLAGS. The guard should cover the expanded flag set.
Verified correct:
- All hex→octal conversions in MIPS and SPARC fcntl.h are numerically correct
- Legacy open()/openat() properly strips OPENAT2_REGULAR via build_open_how() masking with VALID_OPEN_FLAGS
- All filesystem cleanup paths (ceph, gfs2, nfs, cifs) properly handle resources when returning -EFTYPE
- O_DIRECTORY + OPENAT2_REGULAR mutual exclusion is correct
- O_PATH + OPENAT2_REGULAR is properly rejected by O_PATH_FLAGS check
Ruled out:
- NFS -ENOTDIR to -EFTYPE conversion: in atomic_open context, parent path is VFS-resolved, server errors relate to
target
- CIFS resource leak: out_err label properly closes server handle and calls iput()
- OPENAT2_REGULAR + O_TMPFILE: silently accepted but tmpfiles are always regular, so harmless
FINAL REGRESSIONS FOUND: 2
FINAL TOKENS USED: ~45000
False positives eliminated: NFS -ENOTDIR conversion, CIFS resource leak, O_TMPFILE interaction
Cheers,
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply
* Re: [PATCH v5 0/4] OPENAT2_REGULAR flag support for openat2
From: Dorjoy Chowdhury @ 2026-03-16 16:12 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-kernel, linux-api, ceph-devel, gfs2, linux-nfs, linux-cifs,
v9fs, linux-kselftest, viro, brauner, jack, jlayton, chuck.lever,
alex.aring, arnd, adilger, mjguzik, smfrench, richard.henderson,
mattst88, linmag7, tsbogend, James.Bottomley, deller, davem,
andreas, idryomov, amarkuze, slava, agruenba, trondmy, anna,
sfrench, pc, ronniesahlberg, sprasad, tom, bharathsm, shuah,
miklos, hansg
In-Reply-To: <20260307140726.70219-1-dorjoychy111@gmail.com>
Ping...
Requesting for review on this patch series please.
Regards,
Dorjoy
On Sat, Mar 7, 2026 at 8:07 PM Dorjoy Chowdhury <dorjoychy111@gmail.com> wrote:
>
> Hi,
>
> I came upon this "Ability to only open regular files" uapi feature suggestion
> from https://uapi-group.org/kernel-features/#ability-to-only-open-regular-files
> and thought it would be something I could do as a first patch and get to
> know the kernel code a bit better.
>
> The following filesystems have been tested by building and booting the kernel
> x86 bzImage in a Fedora 43 VM in QEMU. I have tested with OPENAT2_REGULAR that
> regular files can be successfully opened and non-regular files (directory, fifo etc)
> return -EFTYPE.
> - btrfs
> - NFS (loopback)
> - SMB (loopback)
>
> Changes in v5:
> - EFTYPE is already used in BSDs mentioned in commit message
> - consistently return -EFTYPE in all filesystems
>
> Changes in v4:
> - changed O_REGULAR to OPENAT2_REGULAR
> - OPENAT2_REGULAR does not affect O_PATH
> - atomic_open codepaths updated to work properly for OPENAT2_REGULAR
> - commit message includes the uapi-group URL
> - v3 is at: https://lore.kernel.org/linux-fsdevel/20260127180109.66691-1-dorjoychy111@gmail.com/T/
>
> Changes in v3:
> - included motivation about O_REGULAR flag in commit message e.g., programs not wanting to be tricked into opening device nodes
> - fixed commit message wrongly referencing ENOTREGULAR instead of ENOTREG
> - fixed the O_REGULAR flag in arch/parisc/include/uapi/asm/fcntl.h from 060000000 to 0100000000
> - added 2 commits converting arch/{mips,sparc}/include/uapi/asm/fcntl.h O_* macros from hex to octal
> - v2 is at: https://lore.kernel.org/linux-fsdevel/20260126154156.55723-1-dorjoychy111@gmail.com/T/
>
> Changes in v2:
> - rename ENOTREGULAR to ENOTREG
> - define ENOTREG in uapi/asm-generic/errno.h (instead of errno-base.h) and in arch/*/include/uapi/asm/errno.h files
> - override O_REGULAR in arch/{alpha,sparc,parisc}/include/uapi/asm/fcntl.h due to clash with include/uapi/asm-generic/fcntl.h
> - I have kept the kselftest but now that O_REGULAR and ENOTREG can have different value on different architectures I am not sure if it's right
> - v1 is at: https://lore.kernel.org/linux-fsdevel/20260125141518.59493-1-dorjoychy111@gmail.com/T/
>
> Thanks.
>
> Regards,
> Dorjoy
>
> Dorjoy Chowdhury (4):
> openat2: new OPENAT2_REGULAR flag support
> kselftest/openat2: test for OPENAT2_REGULAR flag
> sparc/fcntl.h: convert O_* flag macros from hex to octal
> mips/fcntl.h: convert O_* flag macros from hex to octal
>
> arch/alpha/include/uapi/asm/errno.h | 2 +
> arch/alpha/include/uapi/asm/fcntl.h | 1 +
> arch/mips/include/uapi/asm/errno.h | 2 +
> arch/mips/include/uapi/asm/fcntl.h | 22 +++++------
> arch/parisc/include/uapi/asm/errno.h | 2 +
> arch/parisc/include/uapi/asm/fcntl.h | 1 +
> arch/sparc/include/uapi/asm/errno.h | 2 +
> arch/sparc/include/uapi/asm/fcntl.h | 35 +++++++++---------
> fs/ceph/file.c | 4 ++
> fs/gfs2/inode.c | 6 +++
> fs/namei.c | 4 ++
> fs/nfs/dir.c | 4 ++
> fs/open.c | 4 +-
> fs/smb/client/dir.c | 14 ++++++-
> include/linux/fcntl.h | 2 +
> include/uapi/asm-generic/errno.h | 2 +
> include/uapi/asm-generic/fcntl.h | 4 ++
> tools/arch/alpha/include/uapi/asm/errno.h | 2 +
> tools/arch/mips/include/uapi/asm/errno.h | 2 +
> tools/arch/parisc/include/uapi/asm/errno.h | 2 +
> tools/arch/sparc/include/uapi/asm/errno.h | 2 +
> tools/include/uapi/asm-generic/errno.h | 2 +
> .../testing/selftests/openat2/openat2_test.c | 37 ++++++++++++++++++-
> 23 files changed, 127 insertions(+), 31 deletions(-)
>
> --
> 2.53.0
>
^ permalink raw reply
* Re: [PATCH net-next v2 07/14] tcp: honor the maximum advertised window after live retraction
From: Paolo Abeni @ 2026-03-16 11:44 UTC (permalink / raw)
To: atwellwea, netdev, davem, kuba, edumazet, ncardwell
Cc: linux-kernel, linux-api, linux-doc, linux-kselftest,
linux-trace-kernel, mptcp, dsahern, horms, kuniyu, andrew+netdev,
willemdebruijn.kernel, jasowang, skhan, corbet, matttbe,
martineau, geliang, rostedt, mhiramat, mathieu.desnoyers,
0x7f454c46
In-Reply-To: <20260314201348.1786972-8-atwellwea@gmail.com>
On 3/14/26 9:13 PM, atwellwea@gmail.com wrote:
> +/* Sender-visible window rescue does not relax hard receive-memory admission.
> + * If growth did not make room, fall back to the established prune/collapse
> + * path.
> + */
> static int tcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb,
> unsigned int size)
> {
> - if (!tcp_can_ingest(sk, skb) ||
> - !sk_rmem_schedule(sk, skb, size)) {
> + bool can_ingest = tcp_can_ingest(sk, skb);
> + bool scheduled = can_ingest && sk_rmem_schedule(sk, skb, size);
> +
> + if (!scheduled) {
> + int pruned = tcp_prune_queue(sk, skb);
>
> - if (tcp_prune_queue(sk, skb) < 0)
> + if (pruned < 0)
> return -1;
>
> while (!sk_rmem_schedule(sk, skb, size)) {
> - if (!tcp_prune_ofo_queue(sk, skb))
> + bool pruned_ofo = tcp_prune_ofo_queue(sk, skb);
> +
> + if (!pruned_ofo)
> return -1;
> }
> }
The above chunk is AFAICS pure noise. Please have a more careful local
review of this series before any next revision.
/P
^ permalink raw reply
* Re: [PATCH net-next v2 05/14] tcp: grow rcvbuf to back scaled-window quantization slack
From: Paolo Abeni @ 2026-03-16 11:31 UTC (permalink / raw)
To: atwellwea, netdev, davem, kuba, edumazet, ncardwell
Cc: linux-kernel, linux-api, linux-doc, linux-kselftest,
linux-trace-kernel, mptcp, dsahern, horms, kuniyu, andrew+netdev,
willemdebruijn.kernel, jasowang, skhan, corbet, matttbe,
martineau, geliang, rostedt, mhiramat, mathieu.desnoyers,
0x7f454c46
In-Reply-To: <20260314201348.1786972-6-atwellwea@gmail.com>
On 3/14/26 9:13 PM, atwellwea@gmail.com wrote:
> diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
> index 57a2a6daaad3..53781cf591d2 100644
> --- a/net/ipv4/tcp_output.c
> +++ b/net/ipv4/tcp_output.c
> @@ -3375,13 +3375,24 @@ u32 __tcp_select_window(struct sock *sk)
> * scaled window will not line up with the MSS boundary anyway.
> */
> if (tp->rx_opt.rcv_wscale) {
> + int rcv_wscale = 1 << tp->rx_opt.rcv_wscale;
> +
> window = free_space;
>
> /* Advertise enough space so that it won't get scaled away.
> - * Import case: prevent zero window announcement if
> + * Important case: prevent zero-window announcement if
> * 1<<rcv_wscale > mss.
> */
> - window = ALIGN(window, (1 << tp->rx_opt.rcv_wscale));
> + window = ALIGN(window, rcv_wscale);
> +
> + /* Back any scale-quantization slack before we expose it.
> + * Otherwise tcp_can_ingest() can reject data which is still
> + * within the sender-visible window.
> + */
> + if (window > free_space &&
> + (!tcp_rcvbuf_grow_allowed(sk) ||
> + !tcp_try_grow_rcvbuf(sk, tcp_space_from_win(sk, window))))
> + window = round_down(free_space, rcv_wscale);
It looks like this can cause the advertised window to shrink even if we
are in the 'do not allow window to shrink' branch.
Also why the other branch (shrinking allowed) is not touched?
/P
^ permalink raw reply
* Re: [PATCH net-next v2 05/14] tcp: grow rcvbuf to back scaled-window quantization slack
From: Paolo Abeni @ 2026-03-16 11:24 UTC (permalink / raw)
To: atwellwea, netdev, davem, kuba, edumazet, ncardwell
Cc: linux-kernel, linux-api, linux-doc, linux-kselftest,
linux-trace-kernel, mptcp, dsahern, horms, kuniyu, andrew+netdev,
willemdebruijn.kernel, jasowang, skhan, corbet, matttbe,
martineau, geliang, rostedt, mhiramat, mathieu.desnoyers,
0x7f454c46
In-Reply-To: <20260314201348.1786972-6-atwellwea@gmail.com>
On 3/14/26 9:13 PM, atwellwea@gmail.com wrote:
> From: Wesley Atwell <atwellwea@gmail.com>
>
> Teach TCP to grow sk_rcvbuf when scale rounding would otherwise expose
> more sender-visible window than the current hard receive-memory backing
> can cover.
>
> The new helper keeps backlog and memory-pressure limits in the same
> units as the rest of the receive path, while __tcp_select_window()
> backs any rounding slack before advertising it.
>
> Signed-off-by: Wesley Atwell <atwellwea@gmail.com>
> ---
> include/net/tcp.h | 12 ++++++++++++
> net/ipv4/tcp_input.c | 36 ++++++++++++++++++++++++++++++++++--
> net/ipv4/tcp_output.c | 15 +++++++++++++--
> 3 files changed, 59 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index fc22ab6b80d5..5b479ad44f89 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -397,6 +397,7 @@ int tcp_ioctl(struct sock *sk, int cmd, int *karg);
> enum skb_drop_reason tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb);
> void tcp_rcv_established(struct sock *sk, struct sk_buff *skb);
> void tcp_rcvbuf_grow(struct sock *sk, u32 newval);
> +bool tcp_try_grow_rcvbuf(struct sock *sk, int needed);
> void tcp_rcv_space_adjust(struct sock *sk);
> int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp);
> void tcp_twsk_destructor(struct sock *sk);
> @@ -1844,6 +1845,17 @@ static inline int tcp_rwnd_avail(const struct sock *sk)
> return tcp_rmem_avail(sk) - READ_ONCE(sk->sk_backlog.len);
> }
>
> +/* Passive children clone the listener's sk_socket until accept() grafts
> + * their own struct socket,
AFAICS, the above statement is false, see sk_set_socket() in sk_clone()
> so only sockets that point back to themselves
> + * should autotune receive-buffer backing.
> + */
> +static inline bool tcp_rcvbuf_grow_allowed(const struct sock *sk)
> +{
> + struct socket *sock = READ_ONCE(sk->sk_socket);
You can just check `sk->sk_socket`. Also you could re-use this helper in
tcp_data_queue_ofo().
/P
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox