Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions libnvme/src/nvme/fabrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,19 @@ int libnvmf_context_set_device(struct libnvmf_context *fctx, const char *device)
struct libnvme_fabrics_config *libnvmf_context_get_fabrics_config(
struct libnvmf_context *fctx);

/**
* libnvmf_context_set_fabrics_config() - Set fabrics configuration for a
* fabrics context
* @fctx: Fabrics context
* @cfg: Fabrics configuration to apply
*
* Copies the fields of @cfg into the fabrics configuration of @fctx.
*
* Return: 0 on success, or a negative error code on failure.
*/
int libnvmf_context_set_fabrics_config(struct libnvmf_context *fctx,
Comment thread
martin-belanger marked this conversation as resolved.
struct libnvme_fabrics_config *cfg);

/**
* libnvmf_ctrl_get_fabrics_config() - Fabrics configuration of a controller
* @c: Controller instance
Expand Down
34 changes: 34 additions & 0 deletions libnvme/src/nvme/mi.h
Original file line number Diff line number Diff line change
Expand Up @@ -947,3 +947,37 @@
* Return: 0 is a success, nonzero is an error and errno may be read for further details
*/
int libnvme_mi_aem_process(libnvme_mi_ep_t ep, void *userdata);

/**
* libnvme_mi_submit_entry() - Weak hook called before an MI message is sent.
* @type: MCTP message type
* @hdr: Pointer to the MI message header
* @hdr_len: Length of the message header in bytes
* @data: Pointer to message payload data
* @data_len: Length of payload data in bytes
*
* This is a weak symbol that can be overridden by an application to intercept
* outgoing MI messages for tracing or testing purposes. The return value is
* passed back as @user_data to the matching libnvme_mi_submit_exit() call.
*
* Return: An opaque pointer passed to libnvme_mi_submit_exit(), or NULL.
*/
void *libnvme_mi_submit_entry(__u8 type, const struct nvme_mi_msg_hdr *hdr,
Comment thread
igaw marked this conversation as resolved.
size_t hdr_len, const void *data, size_t data_len);

Check failure on line 966 in libnvme/src/nvme/mi.h

View workflow job for this annotation

GitHub Actions / checkpatch review

WARNING: line length of 81 exceeds 80 columns

/**
* libnvme_mi_submit_exit() - Weak hook called after an MI message completes.
* @type: MCTP message type
* @hdr: Pointer to the MI response message header
* @hdr_len: Length of the response message header in bytes
* @data: Pointer to response payload data
* @data_len: Length of response payload data in bytes
* @user_data: Value returned by the matching libnvme_mi_submit_entry() call
*
* This is a weak symbol that can be overridden by an application to intercept
* completed MI transactions. Called with the opaque pointer returned by the
* corresponding libnvme_mi_submit_entry() call.
*/
void libnvme_mi_submit_exit(__u8 type, const struct nvme_mi_msg_hdr *hdr,
size_t hdr_len, const void *data, size_t data_len,
void *user_data);
13 changes: 13 additions & 0 deletions libnvme/test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ if python3_prog.found()
meson.project_source_root() / 'libnvme',
],
)
_ld_args = ['--ld', nvme_ld, '--ld', accessors_ld]
if want_fabrics
_ld_args += ['--ld', nvmf_ld, '--ld', nvmf_accessors_ld]
endif
_hdr_args = []
foreach hdr : headers
_hdr_args += ['--header', meson.project_source_root() / 'libnvme' / 'src' / hdr]
endforeach
test(
'libnvme - check-public-headers',
python3_prog,
args: [files('../tools/check-public-headers.py')] + _ld_args + _hdr_args,
)
endif

# These tests all require interaction with a real NVMe device, so we don't
Expand Down
115 changes: 115 additions & 0 deletions libnvme/tools/check-public-headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of libnvme.
# Copyright (c) 2025, Dell Technologies Inc. or its subsidiaries.
#
# Authors: Martin Belanger <[email protected]>
#
# Verify that every symbol exported in a version script has a prototype
# declared in one of the installed header files.
#
# A __public function that appears in a .ld version script but not in any
# installed header is technically callable by external code, but callers have
# no declaration to include — they would need to write their own prototype or
# use dlsym(), which defeats the purpose of a stable public API.
#
# Usage (via meson — preferred, keeps meson.build as single source of truth):
# python3 tools/check-public-headers.py \
# --ld src/libnvme.ld --ld src/accessors.ld [--ld ...] \
# --header src/nvme/lib.h --header src/nvme/tree.h [--header ...]
#
# Usage (standalone, auto-discovers files from the source root):
# python3 tools/check-public-headers.py [LIBNVME-SOURCE-ROOT]
#
# In auto-discovery mode the script scans src/*.ld for version scripts and
# src/nvme/*.h (excluding files whose name contains "private") for headers.
# The source root defaults to the parent directory of this script.

import argparse
import re
import sys
import pathlib


def parse_args():
parser = argparse.ArgumentParser(
description='Check that every exported symbol has a prototype in an '
'installed header.')
parser.add_argument(
'root', nargs='?',
help='libnvme source root for auto-discovery (defaults to the parent '
'of this script); ignored when --ld / --header are given')
parser.add_argument(
'--ld', action='append', metavar='FILE', dest='ld_files',
help='version-script (.ld) file to read exported symbols from '
'(may be repeated)')
parser.add_argument(
'--header', action='append', metavar='FILE', dest='headers',
help='installed header file to search for prototypes '
'(may be repeated)')
return parser.parse_args()


def main():
args = parse_args()

if args.ld_files or args.headers:
if not args.ld_files or not args.headers:
sys.exit('error: --ld and --header must both be provided together')
ld_files = [pathlib.Path(f) for f in args.ld_files]
headers = [pathlib.Path(f) for f in args.headers]
else:
root = pathlib.Path(args.root) if args.root else \
pathlib.Path(__file__).resolve().parent.parent
src = root / 'src'
ld_files = sorted(src.glob('*.ld'))
headers = sorted(h for h in (src / 'nvme').glob('*.h')
if 'private' not in h.name)

# -----------------------------------------------------------------------
# Collect all symbols listed in the version scripts
# -----------------------------------------------------------------------
ld_syms = {} # symbol -> Path of the .ld file that declares it

for ld_path in ld_files:
for line in ld_path.read_text().splitlines():
m = re.match(r'^\s+([a-z]\w+);', line)
if m:
ld_syms[m.group(1)] = ld_path

# -----------------------------------------------------------------------
# Collect all names that appear as a prototype/declaration in installed
# headers. Match any identifier immediately followed by '(' — this
# catches both single-line and multi-line function declarations, and macro
# definitions that alias a function name. The libnvme_*/libnvmf_*
# namespace is long enough that false positives from comments are not a
# practical concern.
# -----------------------------------------------------------------------
header_syms = set()

for hdr_path in headers:
for m in re.finditer(r'\b([a-z_]\w+)\s*\(', hdr_path.read_text()):
header_syms.add(m.group(1))

# -----------------------------------------------------------------------
# Report exported symbols with no prototype in any installed header
# -----------------------------------------------------------------------
errors = 0

for sym, ld_path in sorted(ld_syms.items()):
if sym not in header_syms:
print(f'ERROR: {sym}() is exported in {ld_path.name} '
f'but has no prototype in any installed header')
errors += 1

if errors:
print(f'\n{errors} error(s) found.')
sys.exit(1)

print(f'OK: all {len(ld_syms)} exported symbols have prototypes '
f'in installed headers.')


if __name__ == '__main__':
main()
Loading