[Nix-dev] Feedback requested: new `boot.kernelPatches` option
Charles Strahan
charles at cstrahan.com
Sun May 1 22:04:20 CEST 2016
Hello,
Nahum Shalman and I were chatting on #nixos, and he was having the same
trouble I once had when trying to apply kernel patches. As a result of
that conversation, I have opened a pull request
(https://github.com/NixOS/nixpkgs/pull/15095) adding support for easily
specifying kernel patches in one's NixOS configuration, and I'd greatly
appreciate some feedback.
Here's an example of using the new boot.kernelPatches option:
boot.kernelPatches = [ pkgs.kernelPatches.ubuntu_fan_4_4 ];
A bit of back story, as well as a description of its use and
implementation:
Back in August of 2015, I needed to apply some kernel patches. The first
thing I did was look to the wiki
(https://nixos.org/wiki/How_to_tweak_Linux_kernel_config_options), which
suggested doing something like this:
nixpkgs.config = {
packageOverrides = pkgs: {
stdenv = pkgs.stdenv // {
platform = pkgs.stdenv.platform // {
kernelPatches = [
{ patch = ../patches/i915_317.patch; name = "i915-fix"; };
{ patch =
../patches/override_for_missing_acs_capabilities.patch;
name = "acs_overrides"; };
];
};
};
};
};
I find that rather unintuitive (if not an outright hack). After studying
the relevant expressions, I amended the wiki page to suggest the
following (which is almost identical to the suggestion in the manual
today):
nixpkgs.config = {
packageOverrides = super: let self = super.pkgs; in {
linux_3_18 = super.linux_3_18.override {
kernelPatches = super.linux_3_18.kernelPatches ++ [
# we'll add the Ubuntu Fan Networking patches from Nixpkgs
self.kernelPatches.ubuntu_fan
# and we'll also add one of our own patches
{ patch = ../patches/i915_317.patch; name = "i915-fix"; }
];
# add "CONFIG_PPP_FILTER y" option to the set of kernel options
extraConfig = "PPP_FILTER y" ;
};
};
};
That's still pretty bad, as we're attempting override the kernel while
hoping that `linux_3_18` is the kernel used by the `linuxPackages`
attribute (which happens to be the default value for the
`boot.kernelPackages` NixOS option) -- too many assumptions being made,
too fragile.
Let's take another look at the new `boot.kernelPatches` option:
boot.kernelPatches = [ pkgs.kernelPatches.ubuntu_fan_4_4 ];
Much better, no?
To implement this option, I also had to add support for overriding the
kernel and/or packages within a given `linuxPackages` set. This is done
by using the new `linuxPackages.override` attribute like so:
pkgs.linuxPackages.override (self: super: {
kernel = super.kernel.override {
kernelPatches = super.kernel.kernelPatches ++ [
pkgs.kernelPatches.ubuntu_fan_4_4 ];
};
});
As a result, setting fine grained options for one's kernel (or adjusting
each of the kernel packages) can look something like this:
boot.kernelPackages = pkgs.linuxPackages.override (self: super: {
kernel = super.kernel.override {
kernelPatches = super.kernel.kernelPatches ++ [
pkgs.kernelPatches.ubuntu_fan_4_4 ];
extraConfig = "PPP_FILTER y" ;
};
});
I think that's a lot cleaner. Granted, the value of
`boot.kernelPackages` will not be the same value as `pkgs.linuxPackages`
(e.g. the value that the rest of nixpkgs refers to), but that's
addressed by doing the following instead:
nixpkgs.config = {
packageOverrides = super: let self = super.pkgs; in {
linuxPackages = super.linuxPackages.override (self: super: {
kernel = super.kernel.override {
kernelPatches = super.kernel.kernelPatches ++ [
pkgs.kernelPatches.ubuntu_fan_4_4 ];
extraConfig = "PPP_FILTER y" ;
};
});
};
};
(Ideally, the `boot.kernelPatches` option would effectively do the
latter, that way the system closure doesn't include two
kernels/kernelPackages: one for the running kernel (i.e.
boot.kernelPackages) and another for every occurrence of `linuxPackages`
within nixpkgs. Regardless, this pull request doesn't make things any
worse off: today, if you set the `boot.kernelPackages` options to
anything other than `pkgs.linuxPackages`, you now have *two* sets of
kernel packages in your system's build-time (and maybe also run-time)
closure. In the future, we might want to find some way to push the value
of `boot.kernelPackages` into nixpkgs, as we don't have a suitable
mechanism for that today.)
After implementing the `override` mechanism for `linuxPackages`, I
realized it would be trivial to factor out a reusable function:
# Create an overridable, recursive attribute set. For example:
#
# nix-repl> obj = makeObject (self: { })
#
# nix-repl> obj
# { override = «lambda»; }
#
# nix-repl> obj = obj.override (self: super: { foo = "foo"; })
#
# nix-repl> obj
# { foo = "foo"; override = «lambda»; }
#
# nix-repl> obj = obj.override (self: super: { foo = super.foo + "
+ "; bar = "bar"; foobar = self.foo + self.bar; })
#
# nix-repl> obj
# { bar = "bar"; foo = "foo + "; foobar = "foo + bar"; override =
«lambda»; }
makeObject = rattrs:
let
construct = rattrs:
fix (self: rattrs self // {
override = f: construct (extends f rattrs);
});
in construct rattrs;
(I'd love suggestions for what to name it -- makeObject was just the
first thing that came to mind.)
One of the nice things about this implementation is that you can chain
as many `.override` as you want, whereas I've seen similar ad hoc
implementations in nixpkgs that allow for specifying overrides once
(e.g. haskellPackages) -- or worse, each `.override` "forgets" the
previous invocation. Aside from the inherent brain-hurt induced by
laziness and fixpoints, I think this implementation is pretty
straightforward and clearly "does the right thing"; I think there would
be value in encapsulating this pattern in nixpkgs once, rather than
having a bunch of ad hoc implementations that vary (quite subtly) in
their semantics.
So, with all of that said, what do you think of the new
`boot.kernelPatches` configuration option?
What are your thoughts on the `makeObject` function, and what would be a
better name for it?
I don't feel comfortable pushing this to master without having some
feedback on the overrides mechanism, as I don't want to see a new
pattern/abstraction get used pervasively in nixpkgs only to
(potentially) find that it was the wrong pattern/abstraction. Any
feedback would be greatly appreciated so I can move forward with the
pull request.
-Charles
More information about the nix-dev
mailing list