A usermode filesystem for Non-Volatile Memory (NVM / Storage Class Memory).
Aerie runs entirely in userspace — no kernel filesystem module required. It maps
a persistent storage pool directly into process address space via mmap and
provides POSIX filesystem semantics through a client library that communicates
with a local server process over RPC.
┌─────────────────────────────────────────┐
│ Application │
│ (links libpxfsc / libkvfsc / libcfsc) │
└────────────────┬────────────────────────┘
│ TCP RPC (rpcnet) or shared-memory RPC (rpcfast)
┌────────────────▼────────────────────────┐
│ pxfs_server / cfs_server / kvfs_server │
│ ├── OSD layer (locking, journaling) │
│ ├── MFS backend (directory/file inodes)│
│ └── SCM pool (mmap'd persistent file)│
└─────────────────────────────────────────┘
The storage pool is a regular file memory-mapped into the process address space.
No kernel module or special hardware is required; SCM write latency can be
emulated in userspace via movnti + busy-spin.
Platform: x86-64 Linux only — the codebase uses
rdtsc, SSEmovnti, and x86 inline assembly. An ARM compiler (targeting ARM) cannot compile it. On Apple Silicon you can build and run via Docker with--platform linux/amd64, which runs an x86-64 GCC toolchain under QEMU emulation (~3-5× slower).
| Name | Server | Client API | Description |
|---|---|---|---|
| pxfs | pxfs_server |
libfs_* |
Full POSIX-compatible filesystem — primary variant |
| rxfs | pxfs_server |
rxfs_* |
Read-only replica client of pxfs |
| cfs | cfs_server |
cfs_* |
Cache filesystem — aggressive client-side caching |
| kvfs | kvfs_server |
kvfs_* |
Key-value store (put/get/del) on the same OSD stack |
All filesystem variants are built on the Object Storage Device (OSD) layer
which provides: distributed lock management (hierarchical + flat), journaling,
versioned object management, and extent/container allocation. ubench_osd
benchmarks the raw OSD layer without any filesystem on top.
| Library | Description |
|---|---|
librpcnet / librpcfast |
RPC transports (TCP / shared memory) |
libbcsclt / libbcssrv |
Basic Communication Services client/server |
libcommon / libscm |
Shared utilities and SCM pool abstraction |
libosdclt / libosdsrv |
OSD client/server |
libmfsclt / libmfssrv |
Metadata filesystem client/server |
libpxfsc / libpxfss |
PXFS client/server |
libcfsc / libcfss |
CFS client/server |
librxfsc |
RXFS read-only client |
libkvfsc / libkvfss |
KVFS client/server |
/* lifecycle */
int libfs_init3(const char* xdst, int debug_level);
int libfs_shutdown();
int libfs_mount(const char* source, const char* target, const char* fstype, uint32_t flags);
/* files */
int libfs_open(const char* path, int flags);
int libfs_open2(const char* path, int flags, mode_t mode);
int libfs_close(int fd);
ssize_t libfs_read(int fd, void* buf, size_t n);
ssize_t libfs_write(int fd, const void* buf, size_t n);
ssize_t libfs_pread(int fd, void* buf, size_t n, off_t offset);
ssize_t libfs_pwrite(int fd, const void* buf, size_t n, off_t offset);
off_t libfs_lseek(int fd, off_t offset, int whence);
int libfs_fsync(int fd);
int libfs_sync();
int libfs_stat(const char* path, struct stat* buf);
/* directories */
int libfs_mkdir(const char* path, int mode);
int libfs_rmdir(const char* path);
int libfs_chdir(const char* path);
char* libfs_getcwd(char* buf, size_t size);
/* namespace */
int libfs_link(const char* oldpath, const char* newpath);
int libfs_unlink(const char* pathname);
int libfs_rename(const char* oldpath, const char* newpath);
/* fd management */
int libfs_dup(int oldfd);
int libfs_dup2(int oldfd, int newfd);int kvfs_init2(const char* xdst);
int kvfs_shutdown();
int kvfs_mount(const char* source, uint32_t flags);
ssize_t kvfs_put(const char* key, const void* buf, size_t count);
ssize_t kvfs_get(const char* key, void* buf);
int kvfs_del(const char* key);
int kvfs_sync();- Linux x86-64, GCC ≥ 4.4 with C++11 support, CMake ≥ 3.16
libconfig++-devlibboost-dev(yum install boost-devel.x86_64orapt install libboost-dev)libsparsehash-dev(google-sparsehash — build with the same compiler)- Python 2.6+ with
sysv_ipcpackage (pip install sysv_ipc; requirespython-devel)
# From repo root — builds libfs + all tests/benchmark binaries
cmake -S . -B build
cmake --build build --parallel $(nproc)
# Or from libfs/ only (same result — top-level delegates here)
cmake -S libfs -B libfs/build
cmake --build libfs/build --parallel $(nproc)What gets compiled:
- All 18 shared libraries (
librpcnet,libpxfsc,libkvfsc, …) - All servers and tools (
pxfs_server,kvfs_server,pool_tool,pxfs_mkfs, …) - All benchmark + API test binaries from
tests/benchmark/(pxfs_api_test,kvfs_api_test,ubench_pxfs, …)
tests/unittest/andtests/integ/are not yet wired into CMake — they depend on a test framework that needs porting to Google Test or Catch2 (see TODO).
Build options (defaults shown):
| Option | Values | Default | Description |
|---|---|---|---|
RPC |
net, fast, fast-two |
net |
RPC transport (net = TCP; fast = shared-memory) |
SCMPOOL |
user, kernel |
user |
Pool allocator (user works without patched kernel) |
BUILD_BENCH |
ON, OFF |
ON |
Build tests/benchmark/ programs |
COVERAGE |
ON, OFF |
OFF |
gcov instrumentation |
SCMPOOL=kernelrequires custom Linux syscalls (312/313) for NVM memory protection, only available on the patched Aerie kernel. UseSCMPOOL=user(the default) for standard Linux.
The Docker image pins to linux/amd64 (via QEMU on Apple Silicon) and exactly
matches the CI environment (Ubuntu 22.04, same compiler, same deps):
# Interactive shell
bash scripts/docker-ci-run.sh bash
# Build
bash scripts/docker-ci-run.sh build
# Format check / fix
bash scripts/docker-ci-run.sh format-code
bash scripts/docker-ci-run.sh format-fix
# Full CI pipeline
bash scripts/docker-ci-run.sh ci-fullNote:
pxfs_serverand other server processes require ASLR to be disabled (kernel.randomize_va_space=0) because the SCM pool usesMAP_FIXEDto remap at its original address. Docker Desktop on Mac cannot set this sysctl, so server-dependent tests only run reliably in CI on real x86-64 Linux. VFS baseline tests run fine locally.
BUILD=libfs/build
export LIBFS_CONFIG=$PWD/libfs/libfs.ini
# 1. Create storage pool
$BUILD/src/scm/pool_tool create -p /tmp/stamnos_pool -s 128M
# 2. Format the filesystem
$BUILD/src/pxfs/pxfs_mkfs create -p /tmp/stamnos_pool -s 128M -t mfs
# 3. Start the server
$BUILD/src/pxfs/pxfs_server -p 10000 -s /tmp/stamnos_pool &
# 4. Run API unit tests
$BUILD/bench/ubench/pxfs_api_test -h 10000
# 5. Run micro-benchmarks
$BUILD/bench/ubench/ubench_pxfs -h 10000 \
+fs_create -n 100 -p /pxfs \
+fs_open -n 100 -p /pxfs \
+fs_read -n 100 -p /pxfs| Flag | Description | Default |
|---|---|---|
-p <port> |
Listen port | 20000 + (pid % 10000) |
-s <path> |
Storage pool path | (required) |
-d <level> |
Debug level (overrides libfs.ini) |
from config |
-l |
Enable lossy RPC for fault testing | off |
| Flag | Description | Default |
|---|---|---|
-h <host:port> |
Server address | 10000 |
-d <level> |
Debug level | 0 |
-l |
Enable lossy RPC | off |
debug:
{
level = 0; # 0 = silent, higher = more verbose
module: { all = False; /* per-subsystem flags */ };
};
ipc:
{
sharedbuffer: { size = "1M"; }; # client↔server shared ring buffer
};
scmmodel:
{
latency = 0; # emulated SCM write latency in nanoseconds
};Every config key can be overridden via environment variable by uppercasing
and replacing . with _:
LIBFS_CONFIG=/path/to/libfs.ini # use a different config file
DEBUG_LEVEL=0 # debug.level
IPC_SHAREDBUFFER_SIZE=4M # ipc.sharedbuffer.size
SCMMODEL_LATENCY=100 # 100 ns write latency emulation
RPC_LOSSY=5 # % packet drop (fault injection)| Binary | Filesystem | Server needed |
|---|---|---|
ubench_pxfs |
pxfs | pxfs_server |
ubench_rxfs |
rxfs (read-only) | pxfs_server |
ubench_cfs |
cfs | cfs_server |
ubench_osd |
OSD layer | pxfs_server |
ubench_vfs |
Linux VFS (baseline) | none |
Available operations: +fs_create, +fs_open, +fs_read, +fs_seqwrite,
+fs_seqread, +fs_randread, +fs_randwrite, +fs_append, +fs_rename,
+fs_unlink, +fs_delete, +fs_fread
Options per operation: -n <count> -p <root_path> -s <size>
Run everything in Docker (handles server lifecycle, ASLR, pool setup):
bash tests/run-tests.sh api # pxfs: 19 API tests (open/read/write/mkdir/stat/…)
bash tests/run-tests.sh kvfs # kvfs: 7 API tests (put/get/del/overwrite/sync/…)
bash tests/run-tests.sh bench # pxfs micro-benchmarks
bash tests/run-tests.sh all # api + bench
bash tests/run-tests.sh leak # pxfs API tests under valgrind leak-check
bash tests/run-tests.sh kvfs-leak # kvfs API tests under valgrind leak-check
bash tests/run-tests.sh shell # drop into build container shellOr manually against a running server:
pxfs_api_test -h 10000
kvfs_api_test -h 10001Note:
tests/unittest/andtests/integ/contain unit and integration test sources but are not yet compiled — the test framework needs porting to Google Test or Catch2 (see TODO).
Every push to master runs three jobs (Ubuntu 22.04, x86-64):
| Job | Flags | What it does |
|---|---|---|
| CMake build | -DRPC=fast -DSCMPOOL=kernel |
Release build, must be warning-free |
| ubench | -DRPC=net -DSCMPOOL=user |
Smoke-tests all filesystem variants; results posted to step summary |
| Docker | — | Builds runtime image; pushes to GHCR on v* tags |
libfs/
src/
bcs/ Basic Communication Services (TCP/shared-mem RPC + IPC)
scm/ SCM abstraction (persistent regions, pool allocator)
osd/ Object Storage Device layer (locking, journaling, versioning)
pxfs/ POSIX filesystem (server + client + mkfs)
cfs/ Cache filesystem variant
rxfs/ Read-only client variant
kvfs/ Key-value store variant
common/ Shared utilities, timing (hrtime), errno
libfs.ini Runtime configuration
tests/
unittest/ Unit tests (OSD containers, lock protocol, SCM, client)
integtest/ Integration tests (BCS, MFS, OSD, CFS, KVFS)
interactive/ Manual/interactive test harnesses
bench/
ubench/ Micro-benchmarks + API tests (CMake-built)
filebench-*/ Filebench workload configs
kvzone-*/ KVzone benchmark adapted for Aerie
sysbench-*/ Sysbench adapted for Aerie
kyotocabinet-*/ KyotoCabinet comparison benchmark
tool/ Test framework (testfw), trace analyzer, perf breakdown
scripts/
docker-ci-run.sh Run CI pipeline locally in Docker
build-local.sh Build in Docker (matches CI environment)
run-tests.sh Build + run servers + API tests in Docker
format-code.sh clang-format check/fix
analyze-code.sh cppcheck static analysis
quality-check.sh format + analyze combined
aerie-server.sh Start pxfs_server with pool init
aerie-bench.sh Run pxfs micro-benchmarks against running server
docker/ Dockerfile, Dockerfile.ci, docker-compose files
linux/
linux-3.9/ Patched Linux 3.9 kernel with SCM syscall support
scmdisk/ Optional kernel block device emulating SCM latency
scmmodel/ SCM latency modeling tools
tools/ Build and utility tools
The linux/linux-3.9/ tree is a forward-port of Sankar's SCM patch
to Linux 3.9. It adds two custom syscalls (312/313) for NVM memory protection.
You only need this if you want to use SCMPOOL=kernel; the default user pool
works on stock Linux.
Compilation requires a native x86-64 Linux environment (Docker/EC2/VM — not macOS):
cd linux/linux-3.9
make mrproper
make x86_64_defconfig # or: make menuconfig (enable CONFIG_SCM=y)
make -j$(nproc)
sudo make modules_install install
sudo update-grub && sudo rebootSCM-specific kernel config options:
CONFIG_SCM=y # Enable SCM support
CONFIG_SCM_DEVICE=y # SCM device driver
The patch modifies 17 files across mm/, kernel/, arch/x86/, and
include/linux/ — all integrated; no manual patching needed.
Client Process Server Process
↓ ↓
pxfs_api_test / kvfs_api_test pxfs_server / kvfs_server
↓ ↓
Client Library (pxfs/client) Server Library (pxfs/server)
↓ ↓
OSD Client Layer ←── RPC ──────→ OSD Server Layer
↓ ↓
BCS (shared-mem IPC) BCS (shared-mem IPC)
↓ ↓
SCM Pool (persistent mmap'd file)
Aerie uses POSIX shared memory for client↔server IPC. Make sure /dev/shm is
mounted (standard on modern Linux; add to /etc/fstab if missing):
shm /dev/shm tmpfs nodev,nosuid,noexec 0 0
Stale shared-memory segments from crashed processes accumulate in /dev/shm —
clean them up periodically with rm -f /dev/shm/shbuf_*.
The codebase follows the Google C++ Style Guide.
Formatting is enforced via clang-format-12 (run bash scripts/format-code.sh --fix).