How to get compatible (hardened) kernel for ZFS module?

Hi there,

I am one of those people who rely on ZFS for their important data. The trouble with ZFS is that the kernel and module need to be compatible, since the newest kernel is not always supported. So it can happen that sometimes you need to wait a while to update the kernel for the ZFS module to catch up.

According to the wiki site about ZFS there is an option to always only install the latest compatible kernel.

boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;

This is indeed very helpful and seems to work perfectly fine. However it installs the latest (regular) kernel while I would like to use the hardened kernel.

I was unfortunately completely unable to find information about the previously mentioned setting. I found no information if there is an setting I could use to get the same behavior for the hardened kernel or where this option even is defined and coming from (so that I could maybe create one for the hardened kernel).

Any help in this regard will be greatly appreciated.

1 Like

The official documentation is here: NixOS 23.11 manual | Nix & NixOS

Which isn’t much more useful either. So let me try!

config.boot.zfs.package.latestCompatibleLinuxPackages is not an option exactly. boot.zfs.package is an option, and when you access it via config you instead get the value it is set to.

In other words, there is no module that defines this, latestCompatibleLinuxPackages is an attribute of the zfs package. This means it must be in the zfs package’s passthru:

Of course, you could also find this just by searching latestCompatibleLinuxPackages on GitHub, but I think understanding what’s going on is useful.

Where is that variable defined then? Well, it’s an argument to the package file, so potentially any number of places. So we need to search the repo: https://github.com/search?q=repo%3ANixOS%2Fnixpkgs%20latestCompatibleLinuxPackages&type=code

So in other words here (technically also in the unstable package, but that isn’t particularly useful or meaningfully different):

So it’s hardcoded by the package maintainer, and there’s no variant of it for the hardened kernel (or any other kernel for that matter).

It’s possible you might be able to abuse a passthru, or the version of the kernel derivation to get the correct hardened kernel for now, but there’s no good way to do what you want right now.

I could imagine a more flexible kernel version picking module/tool that also accounts for nvidia versions and whatnot, but then I’m not sure implementing such a thing would necessarily be a good idea. I think the official stance from upstream is that you should use the latest LTS kernel as packaged by NixOS, as this is the only version that can reasonably be expected to work with third party kernel modules. Either way someone with either time or money needs to do some work to give you what you want :wink:

I have a related problem where I use ZFS on a Surface. The nixos-hardware module defines a set of patched Surface kernels that is separate from the kernels in nixpkgs. I need to select the latest Surface kernel that is compatible with ZFS:

microsoft-surface.kernelVersion =
  with builtins; let
    latestCompatibleVersion = config.boot.zfs.package.latestCompatibleLinuxPackages.kernel.version;
    surfaceVersions = options.microsoft-surface.kernelVersion.type.functor.payload;
    compatibleVersions = filter (x: compareVersions x latestCompatibleVersion <= 0) surfaceVersions;
    orderedCompatibleVersions = sort (x: y: compareVersions x y > 0) compatibleVersions;
  in head orderedCompatibleVersions;

This isn’t perfect because latestCompatibleLinuxPackages is not set to the absolute latest version compatible with ZFS, but rather the latest compatible version that is also packaged in nixpkgs. It’s good enough for me because it will keep me reasonably up-to-date without configuring anything manually.

Note that latestCompatibleLinuxPackages can sometimes go backwards. I had some ZFS machines downgrade from 6.3 to 6.1 because 6.3 went EOL, but 6.4 didn’t support ZFS. The same thing could happen with my Surface versions.

We can modify the above for hardened kernels:

boot.kernelPackages =
  with builtins; with lib; let
    latestCompatibleVersion = config.boot.zfs.package.latestCompatibleLinuxPackages.kernel.version;
    hardenedPackages = filterAttrs (name: packages: hasSuffix "_hardened" name && (tryEval packages).success) pkgs.linuxKernel.packages;
    compatiblePackages = filter (packages: compareVersions packages.kernel.version latestCompatibleVersion <= 0) (attrValues hardenedPackages);
    orderedCompatiblePackages = sort (x: y: compareVersions x.kernel.version y.kernel.version > 0) compatiblePackages;
  in head orderedCompatiblePackages;

We use hasSuffix "_hardened" name to match the hardened kernel package attributes. We use (tryEval packages).success to filter out the obsolete attributes that throw. Then, we filter and sort the package sets as usual.

2 Likes