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
88 changes: 11 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,90 +303,24 @@

### Updating the libnvme accessor functions

libnvme exposes a set of getter and setter functions (accessors) for its core
internal structs (`libnvme_path`, `libnvme_ns`, `libnvme_ctrl`, `libnvme_subsystem`,
`libnvme_host`, `libnvme_fabric_options`). These are generated from the struct
definitions in `libnvme/src/nvme/private.h` by the tool
`libnvme/src/nvme/generate-accessors.c`.
libnvme exposes auto-generated getter/setter accessor functions for its
ABI-stable opaque structs. Two sets of accessors are maintained:

The generated files are committed to the source tree and are **not**
regenerated during a normal build:
| Set | Input header | Generated files |
|-----|-------------|-----------------|
| Common NVMe | `libnvme/src/nvme/private.h` | `src/nvme/accessors.{h,c}`, `src/accessors.ld` |
| NVMe-oF | `libnvme/src/nvme/private-fabrics.h` | `src/nvme/nvmf-accessors.{h,c}`, `src/nvmf-accessors.ld` |

```
libnvme/src/nvme/accessors.h # public API declarations (with Doxygen stubs)
libnvme/src/nvme/accessors.c # implementations
libnvme/src/nvme/accessors.ld # linker version script (manually maintained)
```

#### When to regenerate

Regeneration is needed when a struct member is added, removed, or renamed in
`private.h`, or when a struct is added to or removed from
`generate-accessors-include.list` or excluded in `generate-accessors-exclude.list`.

#### How to regenerate

```shell
$ make update-accessors
```

or equivalently:
The generated `.h` and `.c` files are committed to the source tree and are
**not** regenerated during a normal build. To regenerate after modifying a
`/*!generate-accessors*/` struct:

```shell
$ meson compile -C .build update-accessors
```

The script compiles the generator, runs it, and atomically updates
`accessors.h` and `accessors.c` only when their content changes.
Commit the updated files afterward:

```shell
$ git add libnvme/src/nvme/accessors.h libnvme/src/nvme/accessors.c
$ git commit -m "libnvme: regenerate accessors following <struct> changes"
```

#### Maintaining accessors.ld

`accessors.ld` is a GNU linker version script that controls which accessor
symbols are exported from `libnvme.so` and under which ABI version label they
were introduced (e.g. `LIBNVME_ACCESSORS_3`).

This file is **not** updated automatically, because each symbol must be placed
in the correct version section by the maintainer. Adding a symbol to an
already-published version section would break binary compatibility for
existing users of the library.

When `make update-accessors` detects that the symbol list has drifted from
`accessors.ld`, it prints a report like the following:

```
WARNING: accessors.ld needs manual attention.

Symbols to ADD (place in a new version section, e.g. LIBNVME_ACCESSORS_X_Y):
libnvme_ctrl_get_new_field
libnvme_ctrl_set_new_field
```

New symbols must be added to a **new** version section that chains the
previous one. For example, if the current latest section is
`LIBNVME_ACCESSORS_3_0`, add a new section for the next release:

```
LIBNVME_ACCESSORS_3_1 {
global:
libnvme_ctrl_get_new_field;
libnvme_ctrl_set_new_field;
} LIBNVME_ACCESSORS_3_0;
```

Then commit `accessors.ld` together with the regenerated source files.

#### CI enforcement

A GitHub Actions workflow (`.github/workflows/check-accessors.yml`) runs on
every push and pull request. It regenerates the accessor files and fails if
the result differs from what is committed, ensuring the source tree never
drifts silently.
See [`libnvme/README.md`](libnvme/README.md#accessor-generation) for full

Check failure on line 322 in README.md

View workflow job for this annotation

GitHub Actions / checkpatch review

WARNING: It's generally not useful to have the filename in the file
details, including how to maintain the `.ld` version-script files.

## Dependency

Expand Down
82 changes: 82 additions & 0 deletions libnvme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,85 @@ meson setup .build -Db_sanitize=address && LD_PRELOAD=/lib64/libasan.so.6 ninja
```

It's also possible to enable the undefined behavior sanitizer with `-Db_sanitize=undefined`. To enable both, use `-Db_sanitize=address,undefined`.

## Accessor generation

Some public structs in libnvme use auto-generated setter/getter accessor
functions to provide ABI stability. Callers never access struct members
directly; they use the generated accessors instead. The generated files are
committed to the source tree and are **not** regenerated during a normal build.

Two sets of accessors are maintained — one for common NVMe structs and one for
NVMe-oF-specific structs. The split exists so that non-fabrics (e.g. embedded
or PCIe-only) builds can exclude all fabrics code entirely.

| Meson target | Input header | Generated files |
|---|---|---|
| `update-common-accessors` | `src/nvme/private.h` | `src/nvme/accessors.{h,c}`, `src/accessors.ld` |
| `update-fabrics-accessors` | `src/nvme/private-fabrics.h` | `src/nvme/nvmf-accessors.{h,c}`, `src/nvmf-accessors.ld` |

### When to regenerate

Regeneration is needed whenever a `/*!generate-accessors*/` struct in
`private.h` or `private-fabrics.h` has a member added, removed, or renamed.

### How to regenerate

To regenerate both sets at once:

```bash
meson compile -C .build update-accessors
```

Or regenerate only one set:

```bash
meson compile -C .build update-common-accessors
meson compile -C .build update-fabrics-accessors
```

The script atomically updates the `.h` and `.c` files when their content
changes. Commit the updated files afterward:

```bash
git add libnvme/src/nvme/accessors.h libnvme/src/nvme/accessors.c
git add libnvme/src/nvme/nvmf-accessors.h libnvme/src/nvme/nvmf-accessors.c
git commit -m "libnvme: regenerate accessors following <struct> changes"
```

### Maintaining the .ld version-script files

The `.ld` files (`src/accessors.ld` and `src/nvmf-accessors.ld`) are GNU
linker version scripts that control which accessor symbols are exported from
the shared library and under which ABI version label they were introduced
(e.g. `LIBNVME_ACCESSORS_3`, `LIBNVMF_ACCESSORS_3`).

These files are **not** updated automatically, because each new symbol must be
placed in the correct version section by the maintainer. Adding a symbol to an
already-published version section would break binary compatibility for
existing users of the library.

When the generator detects that the symbol list has drifted, it prints a
report like the following:

```
WARNING: accessors.ld needs manual attention.

Symbols to ADD (new version section, e.g. LIBNVME_ACCESSORS_X_Y):
libnvme_ctrl_get_new_field
libnvme_ctrl_set_new_field
```

New symbols must be added to a **new** version section that chains the
previous one. For example, if the current latest section is
`LIBNVME_ACCESSORS_3`, add:

```
LIBNVME_ACCESSORS_4 {
global:
libnvme_ctrl_get_new_field;
libnvme_ctrl_set_new_field;
} LIBNVME_ACCESSORS_3;
```

Then commit the updated `.ld` file together with the regenerated source files.
11 changes: 9 additions & 2 deletions libnvme/examples/discover-loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ static void print_discover_log(struct nvmf_discovery_log *log)

int main()
{
struct nvmf_discovery_log *log;
struct nvmf_discovery_log *log = NULL;
struct libnvme_global_ctx *ctx;
libnvme_host_t h;
libnvme_ctrl_t c;
int ret;
struct libnvme_fabrics_config cfg;
struct nvmf_discovery_args *args;

nvmf_default_config(&cfg);

Expand Down Expand Up @@ -86,7 +87,13 @@ int main()
return 1;
}

ret = nvmf_get_discovery_log(c, &log, 4);
ret = nvmf_discovery_args_create(&args);
if (!ret) {
nvmf_discovery_args_set_max_retries(args, 4);
ret = nvmf_get_discovery_log(c, args, &log);
nvmf_discovery_args_free(args);
}

libnvme_disconnect_ctrl(c);
libnvme_free_ctrl(c);

Expand Down
21 changes: 9 additions & 12 deletions libnvme/libnvme/nvme.i
Original file line number Diff line number Diff line change
Expand Up @@ -777,25 +777,22 @@ struct libnvme_ns {

%newobject discover;
struct nvmf_discovery_log *discover(int lsp = 0, int max_retries = 6) {
const char *dev;
struct nvmf_discovery_log *logp = NULL;
struct libnvme_get_discovery_args args = {
.c = $self,
.args_size = sizeof(args),
.max_retries = max_retries,
.result = NULL,
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
.lsp = lsp,
};
struct nvmf_discovery_args *args = NULL;

dev = libnvme_ctrl_get_name($self);
if (!dev) {
if (!libnvme_ctrl_get_name($self)) {
discover_err = 1;
return NULL;
}
discover_err = nvmf_discovery_args_create(&args);
if (discover_err)
return NULL;
nvmf_discovery_args_set_lsp(args, lsp);
nvmf_discovery_args_set_max_retries(args, max_retries);
Py_BEGIN_ALLOW_THREADS /* Release Python GIL */
discover_err = nvmf_get_discovery_wargs(&args, &logp);
discover_err = nvmf_get_discovery_log($self, args, &logp);
Py_END_ALLOW_THREADS /* Reacquire Python GIL */
nvmf_discovery_args_free(args);

if (logp == NULL) discover_err = 2;
return logp;
Expand Down
2 changes: 1 addition & 1 deletion libnvme/src/accessors.ld
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ LIBNVME_ACCESSORS_3 {
libnvme_ctrl_get_cntrltype;
libnvme_ctrl_get_cntlid;
libnvme_ctrl_get_dctype;
libnvme_ctrl_get_phy_slot;
libnvme_ctrl_get_host_traddr;
libnvme_ctrl_get_host_iface;
libnvme_ctrl_get_discovery_ctrl;
Expand All @@ -71,7 +72,6 @@ LIBNVME_ACCESSORS_3 {
libnvme_ctrl_set_discovered;
libnvme_ctrl_get_persistent;
libnvme_ctrl_set_persistent;
libnvme_ctrl_get_phy_slot;
libnvme_subsystem_get_name;
libnvme_subsystem_get_sysfs_dir;
libnvme_subsystem_get_subsysnqn;
Expand Down
3 changes: 2 additions & 1 deletion libnvme/src/libnvmf.ld
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ LIBNVMF_3 {
nvmf_exat_ptr_next;
nvmf_free_uri;
nvmf_get_default_trsvcid;
nvmf_discovery_args_create;
nvmf_discovery_args_free;
nvmf_get_discovery_log;
nvmf_get_discovery_wargs;
nvmf_is_registration_supported;
nvmf_nbft_free;
nvmf_nbft_read_files;
Expand Down
10 changes: 8 additions & 2 deletions libnvme/src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ if want_fabrics
sources += [
'nvme/fabrics.c',
'nvme/nbft.c',
'nvme/nvmf-accessors.c',
]
headers += [
'nvme/fabrics.h',
'nvme/nbft.h',
'nvme/nvmf-accessors.h',
]
endif

Expand Down Expand Up @@ -85,6 +87,7 @@ endif
nvme_ld = meson.current_source_dir() / 'libnvme.ld'
nvmf_ld = meson.current_source_dir() / 'libnvmf.ld'
accessors_ld = meson.current_source_dir() / 'accessors.ld'
nvmf_accessors_ld = meson.current_source_dir() / 'nvmf-accessors.ld'

link_args = [
'-Wl,--version-script=@0@'.format(nvme_ld),
Expand All @@ -93,9 +96,12 @@ link_args = [

libconf = configuration_data()
if want_fabrics
link_args += '-Wl,--version-script=@0@'.format(nvmf_ld)
link_args += [
'-Wl,--version-script=@0@'.format(nvmf_ld),
'-Wl,--version-script=@0@'.format(nvmf_accessors_ld),
]
libconf.set('FABRICS_INCLUDE',
'#include <nvme/fabrics.h>\n#include <nvme/nbft.h>')
'#include <nvme/fabrics.h>\n#include <nvme/nbft.h>\n#include <nvme/nvmf-accessors.h>')
else
libconf.set('FABRICS_INCLUDE', '')
endif
Expand Down
Loading
Loading