{ lib, pkgs, config, ... }: let inherit (builtins) toJSON; inherit (lib) optional optionalAttrs mapAttrsToList mkIf withFeature; inherit (lib.my) mkOpt' mkBoolOpt'; rpcOpts = with lib.types; { options = { method = mkOpt' str null "RPC method name."; params = mkOpt' (attrsOf unspecified) { } "RPC params"; }; }; cfg = config.my.spdk; config' = { subsystems = mapAttrsToList (subsystem: c: { inherit subsystem; config = map (rpc: { inherit (rpc) method; } // (optionalAttrs (rpc.params != { }) { inherit (rpc) params; })) c; }) cfg.config.subsystems; }; configJSON = pkgs.writeText "spdk-config.json" (toJSON config'); spdk = pkgs.spdk.overrideAttrs (o: { configureFlags = o.configureFlags ++ (map (withFeature true) [ "rdma" "ublk" ]); buildInputs = o.buildInputs ++ (with pkgs; [ liburing ]); }); spdk-rpc = (pkgs.writeShellScriptBin "spdk-rpc" '' exec ${pkgs.python3}/bin/python3 ${spdk.src}/scripts/rpc.py "$@" ''); spdk-setup = (pkgs.writeShellScriptBin "spdk-setup" '' exec ${spdk.src}/scripts/setup.sh "$@" ''); spdk-debug = pkgs.writeShellApplication { name = "spdk-debug"; runtimeInputs = [ spdk ]; text = '' set -m if [ "$(id -u)" -ne 0 ]; then echo "I need to be root!" exit 1 fi spdk_tgt ${cfg.extraArgs} --wait-for-rpc & until spdk-rpc spdk_get_version > /dev/null; do sleep 0.5 done spdk-rpc bdev_set_options --disable-auto-examine spdk-rpc framework_start_init ${cfg.debugCommands} fg %1 ''; }; in { options.my.spdk = with lib.types; { enable = mkBoolOpt' false "Whether to enable SPDK target."; extraArgs = mkOpt' str "" "Extra arguments to pass to spdk_tgt."; debugCommands = mkOpt' lines "" "Commands to run with the spdk-debug script."; config.subsystems = mkOpt' (attrsOf (listOf (submodule rpcOpts))) { } "Subsystem config / RPCs."; }; config = mkIf cfg.enable { boot.kernelModules = [ "ublk_drv" ]; environment.systemPackages = [ spdk spdk-setup spdk-rpc ] ++ (optional (cfg.debugCommands != "") spdk-debug); systemd.services = { spdk-tgt = { description = "SPDK target"; path = with pkgs; [ bash python3 kmod gawk util-linux ]; serviceConfig = { ExecStartPre = "${spdk.src}/scripts/setup.sh"; ExecStart = "${spdk}/bin/spdk_tgt ${cfg.extraArgs} -c ${configJSON}"; }; wantedBy = [ "multi-user.target" ]; }; }; }; }