Skip to content

PetarKirov/dpdk-d-devshell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dpdk-d-devshell

A Nix-driven workbench for developing DPDK applications in the D programming language.

FeaturesQuick startD + DPDKMicroVMLayout

A flake-parts-based Nix flake that pins the DPDK and LDC toolchains, demonstrates how to call DPDK from D without hand-written bindings or dstep by leaning on LDC's ImportC, and ships a disposable NixOS microvm — preconfigured with hugepages and a virtio NIC — so you can actually run your DPDK code without root, host-side hugepage reservations, or per-distro package juggling. A small upstream C example tags along as a toolchain sanity check.

Features

Call DPDK from D, directly, via ImportC.

examples/helloworld-d/ shows the pattern. A four-line dpdk_c.c #includes the DPDK headers; LDC's ImportC preprocesses it and makes every declaration callable from main.d. No hand-written extern(C) blocks, no dstep step, no binding maintenance:

import dpdk_c;
import core.stdc.stdio : printf;

extern(C) int lcore_hello(void* arg) {
    printf("hello from core %u\n", rte_lcore_id());
    return 0;
}

extern(C) int main(int argc, char** argv) {
    if (rte_eal_init(argc, argv) < 0)
        rte_exit(1, "Cannot init EAL\n");
    //
}

C macros that ImportC can't surface (e.g. RTE_LCORE_FOREACH_WORKER) get spelled out as their underlying rte_get_next_lcore expansion. The Nix derivation drops fortify/fortify3 from NIX_HARDENING_ENABLE because ImportC can't parse glibc's __builtin___*_chk fortified inlines — otherwise everything is stock.

A disposable NixOS microvm for actually running DPDK.

DPDK wants hugepages, and most setups want root. On a dev host that's a pain — system reboots, capability juggling, the works. The flake exposes two QEMU/KVM guests built with microvm.nix:

  • nix run .#dpdk-smoke — boots, runs the C and D helloworlds via a systemd oneshot, prints the output to serial, then powers off cleanly. Drop-in CI smoke test.
  • nix run .#dpdk-guest — boots interactively with serial autologin to root, motd cheat-sheet, and dpdk-testpmd / dpdk-devbind.py ready to go.

Both guests 9p-mount the host's /nix/store into the VM, so microvm.nix skips building an erofs image of the closure — rebuilds are seconds, not minutes. The guest reserves 1 GB of 2 MB hugepages at boot, gets one virtio-net NIC, and runs on stock QEMU+KVM (no host hugepages required).

$ nix run .#dpdk-smoke

[   12.40] dpdk-smoke-start[969]: hello from core 1
[   12.40] dpdk-smoke-start[969]: hello from core 0
[   13.04] dpdk-smoke-start[979]: hello from core 1
[   13.04] dpdk-smoke-start[979]: hello from core 0
[   13.05] dpdk-smoke-start[967]: === smoke done ===
Reproducible toolchain via Nix.

direnv allow (or nix develop) and you have DPDK 25.07, LDC 1.41, dub, dtools, pkg-config, gcc, numactl, and gnumake — no version drift across machines or distros.

$ pkg-config --modversion libdpdk
25.07.0
$ ldc2 --version | head -1
LDC - the LLVM D compiler (1.41.0):
Upstream helloworld in C, byte-for-byte (toolchain reference).

examples/helloworld-c/ is a verbatim copy of DPDK's examples/helloworld/, including the pkg-config libdpdk-driven Makefile. It's not the point of the project — the D port is — but it's a useful reference and a sanity check that the toolchain matches upstream.

$ make -C examples/helloworld-c
$ ldd examples/helloworld-c/build/helloworld | grep librte_eal
        librte_eal.so.25 => /nix/store/…-dpdk-25.07/lib/librte_eal.so.25

Quick start

Get the toolchain.

$ git clone <this repo>
$ cd dpdk-d-devshell
$ direnv allow                 # or, if you don't use direnv:
$ nix develop
Build the D example.

$ make -C examples/helloworld-d

Or as a Nix package:

$ nix build .#helloworld-d
$ ./result/bin/dpdk-helloworld-d --help | head -2
EAL: Detected CPU lcores: 32
EAL: Detected NUMA nodes: 1
Run it in the VM.

$ nix run .#dpdk-smoke      # one-shot: boot, run helloworlds, halt
$ nix run .#dpdk-guest      # interactive shell on serial console

Exit code 0 plus the two hello from core N pairs from the smoke run means the toolchain is healthy end-to-end.

D + DPDK

The full D main.d lives in examples/helloworld-d/main.d; the ImportC shim is examples/helloworld-d/dpdk_c.c. Three ideas worth carrying into a real project:

  • Keep dpdk_c.c thin. It exists so ImportC has a single translation unit to chew on; expose only the headers your D code actually touches. If a header explodes (DPDK does heavy macro work), pull in a smaller one and put a hand-rolled trampoline next to it.
  • Inline C macros at the call site. ImportC doesn't surface preprocessor macros to D. Expand them in D — RTE_LCORE_FOREACH_WORKER becomes a while loop over rte_get_next_lcore.
  • Disable _FORTIFY_SOURCE for ImportC's preprocessor pass — see nix/pkgs/helloworld-d/default.nix for the hardeningDisable trick, or the NIX_HARDENING_ENABLE filter in examples/helloworld-d/Makefile if you're building outside Nix.

MicroVM

The guests live in nix/microvm/:

  • guest.nix — shared base: QEMU/KVM hypervisor, 4 vCPU, 3 GB RAM (avoiding QEMU's exactly-2-GB hang), 1 GB of 2 MB hugepages, one virtio-net interface, helloworld binaries baked into the closure, host /nix/store shared in via 9p.
  • interactive.nix — serial autologin and a motd cheat-sheet.
  • smoke.nix — systemd oneshot that runs the binaries then powers off.
  • default.nix — flake-parts module wiring both guests as nixosConfigurations and as packages.dpdk-{guest,smoke}.

VFIO PCI pass-through is intentionally out of scope here; the guest is meant to validate the toolchain and exercise software vdevs (net_null0, net_pcap0), not to run wire-rate.

Running on the host instead

If you'd rather skip the VM and run DPDK directly on a NixOS host, reserve hugepages via boot.kernelParams:

boot.kernelParams = [
  "default_hugepagesz=2M"
  "hugepagesz=2M"
  "hugepages=512"          # 1 GB of 2 MB pages
];

For real workloads, prefer 1 GB pages (hugepagesz=1G hugepages=N) and add intel_iommu=on iommu=pt (or amd_iommu=on) for VFIO PCI pass-through.

Layout

.
├── examples/
│   ├── helloworld-c/         # verbatim upstream DPDK helloworld (reference)
│   └── helloworld-d/         # the D port via LDC ImportC
├── nix/
│   ├── pkgs/                 # Nix derivations: cpu-checker, helloworld-{c,d}
│   └── microvm/              # flake-parts module: dpdk-guest, dpdk-smoke
├── flake.nix
└── flake.lock

About

Develop DPDK applications in D (via ImportC). Nix-pinned toolchain, microvm for runtime testing

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors