Layer 1 · The big picture

How the repo is laid out.

A top-to-bottom tour of the v26.01 SPDK source tree. The directory shape is a direct reflection of the stack diagram from page 1.1: framework code in lib/, public headers in include/spdk/, optional backends in module/, runnable binaries in app/, and a build system in mk/ that glues it all together.

~12 min read1 diagramprerequisite: 1.1paths are relative to spdk_v26_01_migration/
On this page
  1. The top level: what every directory is for
  2. app/ — the binaries you actually run
  3. lib/ — the framework libraries
  4. module/ — the plugin layer (especially module/bdev/)
  5. include/spdk/ — the public API surface
  6. doc/ — the official documentation
  7. examples/ — small, focused programs
  8. test/, scripts/, mk/ — the rest
  9. The build system: ./configure + make
  10. The Makefile structure: one Makefile per lib, one per module
  11. How an app links against the framework
  12. The include order convention (private vs public)
  13. Edge cases — what breaks when you break the conventions
  14. Why it matters for the rest of the curriculum

The top level: what every directory is for

From a fresh git clone (this is v26.01.0 — the VERSION file is your source of truth), the top of the tree looks like this:

flowchart TB
classDef code fill:#f6f0e3,stroke:#a67c00,color:#1e2a3a
classDef build fill:#faf0e8,stroke:#a04a2a,color:#1e2a3a
classDef doc fill:#fdfbf6,stroke:#6b7790,color:#1e2a3a
classDef sub fill:#1a2842,stroke:#2a6b6b,color:#e6e9f0

ROOT["spdk_v26_01_migration/"]
ROOT --> A["app/"]:::code
ROOT --> B["lib/"]:::code
ROOT --> C["module/"]:::code
ROOT --> D["include/spdk/"]:::code
ROOT --> E["examples/"]:::code
ROOT --> F["doc/"]:::doc
ROOT --> G["test/"]:::doc
ROOT --> H["scripts/"]:::doc
ROOT --> I["mk/"]:::code
ROOT --> J["Makefile + configure + CONFIG"]:::code

ROOT --> K1["dpdk/"]:::sub
ROOT --> K2["isa-l/ isa-l-crypto/"]:::sub
ROOT --> K3["libvfio-user/"]:::sub
ROOT --> K4["xnvme/ intel-ipsec-mb/"]:::sub
ROOT --> K5["ocf/"]:::sub

ROOT --> L1["dpdkbuild/ isalbuild/"]:::build
ROOT --> L2["isalcryptobuild/ vfiouserbuild/"]:::build
ROOT --> L3["xnvmebuild/ ipsecbuild/"]:::build

ROOT --> M1["python/ go/ proto/"]:::doc
ROOT --> M2["schema/ rpmbuild/"]:::doc
ROOT --> M3["shared_lib/ docker/ patches/"]:::doc
fig. 1 — top-level SPDK repo tree · tap or scroll to zoom · ↗ for fullscreen

fig. 1   Top-level SPDK tree. Source code (gold) lives in app/, lib/, module/, examples/, and include/. Build helpers (rust) are in mk/, configure, and the top-level Makefile. Submodules (teal) and their build outputs (also rust) are alongside. Everything else is docs and packaging.

The shape to internalize: code lives in lib/, module/, app/, examples/, and include/. The other directories are either build infrastructure, vendored submodules, or documentation.

app/ — the binaries you actually run

app/ is the smallest "real" directory. Each subdirectory builds exactly one binary; the binary lands in build/bin/ after make. Here's what's in it:

AppWhat it doesWhen you'd run it
nvmf_tgtThe NVMe-oF target — exposes local bdevs as NVMe namespaces over TCP, RDMA, FC, or VFIO-user.Production deployments exposing remote storage; almost all of Layer 6 is about this binary.
vhostThe virtio target — serves bdevs to QEMU / Cloud Hypervisor / Firecracker over vhost-user sockets.Local VM deployments; covered in Layer 7.1.
iscsi_tgtThe iSCSI target — exposes bdevs as iSCSI LUNs.Legacy environments; rarely the right answer for new builds.
spdk_tgtA combined target that dynamically loads bdev modules and protocol plugins at runtime.Convenience build when you don't want to pick a specific target at build time.
bdevperfThe benchmark tool — a spdk bdev workload generator.Capacity + IOPS testing; the official answer to "is my SPDK build actually fast?"
spdk_topA top-like tool for live reactor inspection.Ops + debugging; see Layer 9.1.
spdk_nvme_perf, spdk_nvme_identify, spdk_nvme_discoverNVMe-specific tools: throughput benchmark, controller info, NVMe-oF discovery.Hardware bring-up, troubleshooting a misbehaving SSD.
spdk_ddLike dd, but for SPDK bdevs.Quick copy/inflate of a bdev.
spdk_lspciList the PCI devices in the system, with the SPDK-relevant info (NUMA node, BAR addresses).Pre-flight check before binding devices to DPDK.
trace, trace_recordUserspace tracing tools (see Layer 9.2).Production tracing and postmortem analysis.
fio/The SPDK fio plugin — built only with --with-fio.If you want fio to drive SPDK bdevs as a job source.

Every entry above is one subdirectory of app/. For example, app/nvmf_tgt/ contains just two files: nvmf_main.c (the actual program) and a Makefile that wires it up. That's it. There is no app/nvmf_tgt/lib/, no app/nvmf_tgt/include/. An SPDK app is intentionally tiny.

lib/ — the framework libraries

lib/ is the heart of SPDK. Each subdirectory is a single C library — code, headers, a Makefile, and a linker version script (spdk_*.map). Here is the full list, with the one-line role of each:

LibraryRoleWhat you'll find inside
lib/eventThe reactor + spdk_app framework.reactor.c, app.c, scheduler_static.c, app_rpc.c. See Layer 2.1.
lib/threadThe threading abstraction (spdk_thread, spdk_io_channel, pollers).thread.c, iobuf.c. See Layer 2.2.
lib/bdevThe bdev framework — the plugin bus for block devices.bdev.c, bdev_rpc.c, bdev_zone.c, part.c, scsi_nvme.c. See Layer 4.
lib/nvmeThe raw NVMe driver — talks to NVMe controllers, manages I/O queues.Controller enumeration, queue pair management, namespace discovery, PRP/SGL handling. Public API in include/spdk/nvme.h.
lib/nvmfThe NVMe-oF target library — the C API for building an NVMe-oF target.nvmf.c, subsystem.c, ctrlr.c, ctrlr_bdev.c, discovery.c, tcp.c, rdma.c, fc.c, vfio_user.c, mdns_server.c, auth.c. See Layer 6.
lib/vhostThe vhost target library — the C API for building a virtio target.virtio-scsi, virtio-blk, vhost-scsi, vhost-blk. See Layer 7.
lib/jsonrpcThe JSON-RPC client and server libraries.jsonrpc_server.c, jsonrpc_client.c, plus *_tcp.c variants. See Layer 3.
lib/rpcHigher-level RPC helpers — registering handlers, defining the JSON schema.The macro-and-function machinery that turns SPDK_RPC_REGISTER into JSON-RPC methods.
lib/blob + lib/lvolThe blobstore (a key-value on top of bdevs) and lvol (the logical volume manager built on blobstore).See Layer 5.
lib/sockThe socket abstraction — POSIX, UDP, RDMA transports behind a single API.Used by nvmf (TCP) and vhost (control plane).
lib/iscsiThe iSCSI target library.Build only with --with-iscsi-initiator for the initiator path.
lib/log, lib/util, lib/confLogging, generic utilities, the configuration-file parser.log.c, util/*.c, the INI-style spdk_conf reader.
lib/initThe framework initializer — coordinates startup of all subsystems.The thing that walks spdk_subsystem_* registrations and brings them up in order.
lib/accel, lib/idxd, lib/ioat, lib/ae4dmaCopy / crypto offload abstractions and their Intel-specific backends.Used by bdev modules that want to do CRC or crypto in hardware.
lib/dma, lib/env_dpdk, lib/env_ocfThe "environment" abstractions — DMA memory, device access, primary execution environment.Every other library depends on these. The default env_dpdk is the only one most users will ever touch.
lib/trace, lib/trace_parserUserspace tracing framework (ftrace-style) and the parser that reads trace files.See Layer 9.2.
lib/notifyAsynchronous change notification — for hotplug events, device changes, etc.Used by the NVMe bdev module to react to device add/remove.
lib/ftl, lib/scsi, lib/virtio, lib/nbd, lib/fuse_dispatcher, lib/keyring, lib/rdma_*, lib/vfio_user, lib/vfu_tgt, lib/vmd, lib/ublk, lib/fsdevMore framework libraries, each backing a specific subsystem.You'll encounter these only when you go deep into the corresponding subsystem.
lib/ut, lib/ut_mockUnit-test scaffolding and mock helpers.Used only by test/unit/.

The naming convention is consistent: spdk_<libname>.map is the linker version script that defines the ABI surface, Makefile is the build script, and the public C API ends up mirrored in include/spdk/. Internal helpers stay private to the library.

module/ — the plugin layer (especially module/bdev/)

module/ is where the optional backends live. The directory shape mirrors lib/: one subdirectory per plugin family, and inside each, one subdirectory per plugin. The largest is module/bdev/, with one directory per bdev backend.

Listing out the bdev backends as of v26.01: aio/ (Linux AIO to a file or block device), crypto/ (ISA-L-crypto-backed encryption), daos/ (--with-daos), delay/ (artificial-latency for testing), error/ (always-fails for fault-injection testing), ftl/ (FTL over NAND), gpt/ (GPT partition parser), iscsi/ (iSCSI initiator as a bdev, --with-iscsi-initiator), lvol/ (logical volumes over blobstore), malloc/ (RAM-backed, volatile, fast, great for tests), nvme/ (NVMe SSD — the canonical backend), null/ (always succeeds, drops the data), ocf/ (OCF cache, --with-ocf), passthru/ (proxies I/O to another bdev), raid/ (software RAID across bdevs), rbd/ (Ceph RBD, --with-rbd), split/ (multiplexes one bdev into many), uring/ (io_uring backend, --with-uring), virtio/ (virtio-pci, --with-virtio), xnvme/ (--with-xnvme), and zone_block/ (ZNS zoned block devices).

Each of these is a separate library with the same shape as lib/<name>: a directory, a Makefile, source files, and a public header for any module that wants to depend on it. For example, module/bdev/nvme/ is its own library (libbdev_nvme.a) that links against libbdev.a and libnvme.a.

The other module/ subdirectories follow the same pattern, just for different kinds of plugins:

  • module/accel/ — backends for the accel framework: dpdk_cryptodev, dsa, iaa, ioat, mlx5, cuda, ae4dma, error.

  • module/sock/ — socket plugins: posix (always built) and uring (io_uring-accelerated).

  • module/event/ — additional reactor subsystems: subsystems is the standard spdk_subsystem_* registration set.

  • module/scheduler/ — alternative thread schedulers (e.g. dynamic, greedy).

  • module/keyring/ — keyring backends (for encrypted bdevs).

  • module/blob/ — alternative blob stores (in addition to lib/blob).

  • module/vfu_device/ — vfio-user device backends.

  • module/env_dpdk/ — the env layer plugin that wires DPDK into spdk_env. Most users never touch this; it builds automatically.

  • module/fsdev/ — filesystem-style device plugins (for FUSE-style exposure).

The single most important Makefile to read in this whole tree is module/bdev/Makefile, because it shows the "subdirs" pattern — how a parent Makefile delegates to its children:

include/spdk/ — the public API surface

include/spdk/ is the only directory in the repo that downstream code is expected to #include from. It contains the public C API for every framework library, plus a few headers that aren't tied to a specific library (stdinc.h, env.h, log.h).

As of v26.01, the public headers number around 100. The full list is too long to enumerate here, but the shape is:

HeaderPublic surface ofLayer in the curriculum
spdk/bdev.hThe bdev framework: spdk_bdev, spdk_bdev_io, spdk_bdev_open, module registration.Layer 4
spdk/nvme.hThe raw NVMe driver: spdk_nvme_probe, spdk_nvme_ctrlr, spdk_nvme_ns, queue pair ops.Layer 0.2 + Layer 6
spdk/nvmf.hThe NVMe-oF target: spdk_nvmf_tgt, spdk_nvmf_subsystem, spdk_nvmf_poll_group.Layer 6
spdk/vhost.hThe vhost target: spdk_vhost_session, spdk_vhost_dev, virtio config space helpers.Layer 7
spdk/jsonrpc.hThe JSON-RPC client and server.Layer 3
spdk/rpc.hThe RPC handler-registration macros.Layer 3
spdk/thread.hspdk_thread, spdk_io_channel, pollers, spdk_poller.Layer 2
spdk/event.hspdk_app_start, spdk_app_stop, the reactor API.Layer 2
spdk/blob.h, spdk/blob_bdev.h, spdk/lvol.hThe blobstore and lvol APIs.Layer 5
spdk/env.h, spdk/stdinc.h, spdk/log.h, spdk/memory.h, spdk/mmio.hCross-cutting utilities — environment, stdint shim, logging, memory allocation, MMIO access.Used everywhere
spdk/nvme_spec.h, spdk/nvme_zns.h, spdk/nvme_ocssd.h, spdk/scsi_spec.h, spdk/opal_spec.h, spdk/nvmf_spec.hNVMe / SCSI / NVMe-oF / Opal spec constants and structs — the spec-level, not the driver-level, API.Layer 0.2 + 6
spdk/accel.h, spdk/idxd.h, spdk/ioat.h, spdk/dma.h, spdk/dif.hCopy / crypto offload, DMA, Data Integrity Field.Used by bdev modules + storage appliances
spdk/init.hThe framework-init subsystem registration API.Used by all apps
spdk/scheduler.h, spdk/trace.h, spdk/notify.h, spdk/keyring.h, spdk/conf.h, spdk/sock.h, spdk/net.h, spdk/file.h, spdk/pipe.h, spdk/fd.h, spdk/fd_group.hSmaller utilities and integrations.Used as needed
spdk/module/bdev/, spdk/module/keyring/Public headers for module implementations — your bdev module will include headers from here.Layer 8

doc/ — the official documentation

doc/ is the Doxygen + Markdown source for the official SPDK documentation. The most useful files for a new reader:

  • doc/about.md — the one-paragraph elevator pitch. Read this first.

  • doc/overview.md — the structural overview, with the directory layout and the threading model explained.

  • doc/concepts.md — the core concepts (channels, pollers, messages, subsystems).

  • doc/bdev.md — the bdev framework reference.

  • doc/nvme.md — the NVMe driver reference.

  • doc/nvmf.md — the NVMe-oF target reference.

  • doc/vhost.md — the vhost target reference.

  • doc/jsonrpc.md.jinja2 — the JSON-RPC method reference (auto-generated from the schemas in schema/).

  • doc/getting_started.md — how to build and run SPDK end-to-end.

  • doc/concurrency.md — the threading model explained in detail. (You'll read this again in Layer 2.4.)

  • doc/Doxyfile — the Doxygen configuration that builds the API reference at spdk.io/doc/.

examples/ — small, focused programs

examples/ mirrors app/, but for educational and reference programs. Each subdirectory is one small program. The structure:

The list of subdirectories, by topic: bdev/ for bdev examples (includes hello_world/ — the smallest possible bdev program — and bdevperf/ — the bdevperf workload generator), nvme/ for raw NVMe driver examples (hello_world/, nvme_manage/, hotplug/, and several more), nvmf/ for NVMe-oF client (initiator) examples, sock/ for socket abstraction examples, accel/ for copy + crypto offload examples, blob/ for blobstore examples, thread/ for threading examples, idxd/ for IDXD offload examples, ioat/ for IOAT offload examples, interrupt_tgt/ for an example target that runs in interrupt mode, vmd/ for VMD examples, fsdev/ for FUSE-style filesystem device examples, go/ for Go client examples, and util/ for miscellaneous utilities used by other examples.

The hello_world/ programs are the most useful: they do the absolute minimum (open a bdev, submit one I/O, exit) and are heavily commented. examples/bdev/hello_world/hello_bdev.c is the canonical "first program" for the bdev API.

Examples are built with CONFIG_EXAMPLES=y (the default), and their binaries land in build/examples/ — note the separate subdirectory from app/'s build/bin/. The spdk.app.mk Makefile fragment handles the path automatically based on where your Makefile lives.

test/, scripts/, mk/ — the rest

The remaining three top-level directories matter, but in a more focused way.

test/

Functional and unit tests, organized by subsystem: test/bdev/, test/nvme/, test/nvmf/, test/lvol/, test/event/, test/vhost/, test/unit/, and so on. The functional tests are shell scripts (driven by test/common/); the unit tests are C programs built with CONFIG_UNIT_TESTS=y and run via test/unit/unittest.sh. If you change a library behavior, the matching test/<name>/ directory is where to look first.

scripts/

Operational and build helpers. The ones you'll touch:

  • scripts/pkgdep.sh — install all the system dependencies for a given distro. Run this on a fresh VM.

  • scripts/setup.sh — bind NVMe devices to the kernel driver SPDK will replace (e.g. vfio-pci or uio_pci_generic), and allocate hugepages.

  • scripts/rpc.py — the official Python CLI for SPDK's JSON-RPC. This is the Layer 3 equivalent of curl.

  • scripts/gen_ftl.sh, scripts/gen_nvme.sh, scripts/genconfig.py, scripts/genrpc.py — code generators used at build time.

  • scripts/check_format.sh — astyle-based style check. CI runs this; your PR will fail if you don't.

  • scripts/spdkcli.py — an interactive CLI for SPDK. (Mostly superseded by rpc.py but still shipped.)

  • scripts/bpftrace.sh, scripts/bpf/ — bpftrace-based live tracing.

  • scripts/bash-completion/ — bash completion for SPDK's tools.

mk/

The build "library" — small Makefile fragments that every subdirectory's Makefile includes. The fragments:

  • mk/spdk.common.mk — the base fragment included by every Makefile in the tree. Defines the compiler flags, the CONFIG_ variables, the path to include/spdk/config.h, and the helpers (Q, E, M for quiet builds).

  • mk/spdk.lib.mk — the rules for building a library (libspdk_<name>.a and the matching .so if CONFIG_SHARED=y).

  • mk/spdk.app.mk — the rules for building an app or example.

  • mk/spdk.app_vars.mk — the variables that spdk.app.mk expects to be set.

  • mk/spdk.modules.mk — the master list of ALL_MODULES_LIST, plus per-category lists (BLOCKDEV_MODULES_LIST, ACCEL_MODULES_LIST, etc.). Apps use this.

  • mk/spdk.subdirs.mk — the recursive descent into subdirectories, used by module/bdev/Makefile and friends.

  • mk/spdk.deps.mk and mk/spdk.lib_deps.mk — the library-dependency graph. Adding a new dependency between libraries is a one-line edit here.

  • mk/spdk.unittest.mk, mk/spdk.mock.unittest.mk, mk/spdk.fio.mk, mk/nvme.libtest.mk — specialized fragments for unit tests, mocks, the fio plugin, and the NVMe test harness.

The single most useful thing you can do, on day one, is read mk/spdk.common.mk in full. It's about 400 lines, and it tells you everything about how SPDK thinks about flags, includes, and the build environment.

The build system: ./configure + make

The build is the standard autoconf-shaped two-step:

STEP 01
./configure
Resolves dependencies, sets CONFIG_* flags, generates CONFIG + include/spdk/config.h
STEP 02
make
Walks DIRS-y in the top Makefile, each subdir runs its own Makefile
STEP 03
Output
build/bin/, build/examples/, build/lib/

./configure is a 1450-line bash script. It checks for system packages, asks the kernel for hugepage availability, figures out the DPDK layout, and writes a resolved CONFIG file. The relevant knobs you'll touch most often (lifted from ./configure --help):

FlagEffect
--prefix=pathInstall destination (default /usr/local).
--with-dpdk=DIRUse a custom DPDK build instead of the bundled submodule.
--with-rdmaBuild the RDMA transport (requires libibverbs).
--with-vhostBuild the vhost target (enabled by default).
--with-virtioBuild the virtio-pci bdev and the vhost initiator.
--with-vfio-userBuild the custom vfio-user transport for nvmf.
--with-rbdBuild the Ceph RBD bdev module.
--with-cryptoBuild ISA-L-crypto and the vbdev crypto module.
--with-sharedBuild shared libraries (libspdk_*.so) in addition to the static archives.
--disable-tests, --disable-unit-tests, --disable-examples, --disable-appsSkip the corresponding subset of the build.
--enable-debugTurn on debug logging and disable optimizations.
--enable-asan, --enable-ubsan, --enable-tsanEnable the corresponding sanitizer.
--enable-ltoLink-time optimization (slower build, faster binary).
--enable-werrorTreat warnings as errors. Default n in SPDK; some downstream forks turn it on.
--with-cuda, --with-daos, --with-xnvme, --with-uring, --with-iscsi-initiator, --with-ublkToggle the more specialized modules.

What configure does not do: it does not invoke make. You run them in sequence. A clean ./configure && make -j$(nproc) on a 16-core box is on the order of 5–10 minutes.

The Makefile structure: one Makefile per lib, one per module

Every directory that produces a build artifact has its own Makefile. Every Makefile in SPDK looks roughly like one of three patterns. A library, an app, and a subdirs parent. (The full templates are in the next callout; reading them is the fastest way to internalize the pattern.)

The three top-level Makefile fragments (spdk.lib.mk, spdk.app.mk, spdk.subdirs.mk) encode almost all the "what does a build look like" knowledge. Once you've seen one lib, one app, and one subdirs Makefile, you've seen the pattern. The variations are all in the values — SPDK_LIB_LIST, DIRS-y, C_SRCS, SO_VER.

How an app links against the framework

Linking an app is the simplest part of the build, because the Makefile machinery has already done the hard work. Here is the sequence in detail:

  1. Each lib/<name> builds libspdk_<name>.a (and .so if shared). The .a contains the .c files listed in C_SRCS, plus the public symbols declared in spdk_<name>.map.

  2. Each module/<category>/<name> builds its own static archive, named after LIBNAME in its Makefile. For module/bdev/nvme/, that produces libbdev_nvme.a.

  3. The app's Makefile lists SPDK_LIB_LIST. This is the manifest of "what I want to link against."

  4. mk/spdk.app.mk resolves that list into the right -l and -L arguments, ordered to satisfy dependencies. The order is computed from mk/spdk.lib_deps.mk and mk/spdk.modules.mk; you don't compute it.

  5. make produces the binary at build/bin/<appname> (for app/) or build/examples/<appname> (for examples/). The spdk.app.mk Makefile fragment picks the destination based on the directory path.

The whole point of this design is that an SPDK app Makefile is short, readable, and a pure manifest. You almost never need to write a $(CC) invocation by hand.

The include order convention (private vs public)

Every C file in SPDK follows the same include order. The convention is not documented in one place — it's enforced by code review and the astyle-based check — but once you know it, you can read any C file in the tree:

  1. The file's own private header first. If lib/bdev/bdev.c includes a private header, it's "bdev_internal.h" — relative, no spdk/ prefix. This catches the case where you forgot to add the new private header to the include path.

  2. Public spdk/ headers in alphabetical order. spdk/bdev.h, then spdk/env.h, then spdk/json.h, etc. This makes the dependency graph visible at a glance.

  3. System headers last. <stdio.h>, <string.h>, etc. The public spdk/stdinc.h shim is included via spdk/<name>.h, so most SPDK files don't include stdint.h directly.

Public headers in include/spdk/ only include other public headers, plus standard C headers. They never include lib/<name>/<name>_internal.h. That is the single rule that keeps the public ABI clean.

Edge cases — what breaks when you break the conventions

The build system is forgiving, but there are a few specific ways to break it. These are the ones that cost you an afternoon if you don't know about them in advance.

You added a .c to lib/<name> and forgot the Makefile

Symptom: the build succeeds, but the new function is undefined at link time when something else references it.

Cause: the C_SRCS variable in lib/<name>/Makefile is the manifest. A new .c file is not picked up automatically.

Fix: add it to C_SRCS in the right Makefile. Run make clean && make on the affected library, then re-link.

You changed a public header signature without updating callers

Symptom: every .c file that includes the header fails to compile.

Cause: the header is the contract. Changing a function signature in include/spdk/foo.h is, definitionally, an ABI break.

Fix: update all callers (in lib/, module/, app/, examples/) in the same patch. For downstream consumers, bump SO_VER in the relevant library Makefile. The linker version script will then refuse to link the old and new .so together.

You added a new lib/<name>/ without telling lib_deps.mk

Symptom: an app that wants to link against your new library fails to link with an "undefined reference" to a symbol that should be in another library.

Cause: the linker order comes from mk/spdk.lib_deps.mk. New libraries aren't picked up automatically; their dependencies on other libraries need to be declared.

Fix: add the appropriate DEPS lines in mk/spdk.lib_deps.mk. The spdk.lib.mk rules are strict about ordering, so this is one of the most common "I added a new library" gotchas.

You added a new bdev module and forgot to add it to BLOCKDEV_MODULES_LIST

Symptom: your new module compiles (because you added it to module/bdev/Makefile DIRS), but apps that use $(ALL_MODULES_LIST) don't see it.

Cause: BLOCKDEV_MODULES_LIST in mk/spdk.modules.mk is the master list. Apps that want "all the bdev modules" link against that list, not against the directory tree.

Fix: add the LIBNAME to BLOCKDEV_MODULES_LIST in mk/spdk.modules.mk. (The directory tree in module/bdev/Makefile only determines whether the module builds; whether it's linked by an app is a separate question.)

Why it matters for the rest of the curriculum

The directory shape you just read is the map you'll carry through every later layer:

  • Layer 2 (threading) lives in lib/event/ + lib/thread/. When a page says "see the reactor," it means lib/event/reactor.c.

  • Layer 4 (bdev) lives in lib/bdev/ + module/bdev/*. The framework is in lib/; the backends are in module/.

  • Layer 6 (nvmf) lives in lib/nvmf/ + app/nvmf_tgt/. The library is the framework; the app is one possible binary.

  • Layer 8 (write a bdev) is the moment you create a new directory under module/bdev/, add it to module/bdev/Makefile DIRS, add its LIBNAME to BLOCKDEV_MODULES_LIST in mk/spdk.modules.mk, and write a Makefile that uses spdk.lib.mk. Every step is exactly what you saw on this page.

The next layer — 2.1 — The reactor poll loop — opens lib/event/reactor.c and walks through what spdk_app_start actually does. The directory structure from this page is the table of contents for that walk.