There has been some noise around the linux project because Linux Torvalds, its leader, has started using LLMs for writing patches.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c0f3697118eb3a708ea79175f7f07c43339bfab

And this is not his first loud opinion in the matter. He wants people not to complain about it:

https://lore.kernel.org/linux-media/CAHk-=wi4zC+Ze8e+p3tMv8TtG_80KzsZ1syL9anBtmEh5Z40vg@mail.gmail.com/

While I think using LLMs for complex code is a negative thing as it introduces more complexity through losing context while wasting energy, I don’t think I have enough stamina to switch to some BSD distribution for now.

I think this post from David Chisnall summarises the idea quite well:

Claude lacks proper context of the use of the software but spots a pattern that looks like things it’s training set has flagged as slow and so points to the slow path as a bottleneck.

The user asks for a test case and Claude provides a simple microbenchmark that triggers the slow path.

Now that it’s confirmed as slow, the user asks a ‘agentic’ harness to explore the design space and optimise for the benchmark. The swarm of Claude instances makes plausible changes to the code and reports back with a modified version that passes the test suite and improves the microbenchmark.

The test suite doesn’t exhaustively cover all possible race conditions and so the new version may include subtle concurrency bugs. It also probably makes the common case slower and so is a regression overall.

What I can do is to reduce the surface of slop by using less features in the kernel, so I decided to go ahead with nix configuration.

Custom kernel with nixos

I followed this wiki entry:

https://nixos.wiki/wiki/Linux_kernel#Custom_configuration

and tried with a few parameters using the boot.kernelPatches option.

The first thing to note is that the example uses the string syntax

 boot.kernelPatches = [ {
        name = "crashdump-config";
        patch = null;
        extraConfig = ''
          CRASH_DUMP y
        ''
        }];

However later in the documentation they recommend to use structuredExtraConfig which looks like:

  boot.kernelPatches = [ {
    name = "simplified-config";
    patch = null;
    structuredExtraConfig = with lib.kernel; {
        ACPI_HOTPLUG_MEMORY = lib.mkForce unset;
        AMD_HFI = lib.mkForce unset;
        AMD_MEM_ENCRYPT = lib.mkForce unset;
        B43LEGACY = no; # don't have it
    }];

They also recommend using ignoreConfigErrors to avoid error: unused option during build. But I chose to ignore them manually using the lib.mkForce unset that you see above.

Something else I had trouble with: I couldn’t find documentation for how to set a literal. I found this in some nix definition and adapted to the list:

NR_CPUS = lib.mkForce (freeform "8"); # I have 4 cpus * 2 HT

Removing modules

As I got some options set, I decided to run and see how long would it take. Unfortunately the default for a generic distribution kernel is to have everything as a module to support as many configurations as possible. So more than 1 hour in my i3.

I decided to use lsmod to see which modules I had running in the system, and then gunzip /proc/config.gz to see the existing configuration. Using some vim to get both files I managed to match most of the modules I was using against the current kernel configuration, and change every other module to “no”.

My configuration.nix looked like 7000 lines of this:

    structuredExtraConfig = with lib.kernel; {
    ...
        #INET_AH=no;
        #INET_ESP=no;
        #INET_ESP_OFFLOAD=no;
        #INET_IPCOMP=no;
        #INET_XFRM_TUNNEL=no;
        #INET_TUNNEL=no;
        #INET_DIAG=no;
        #INET_TCP_DIAG=no;
        #INET_UDP_DIAG=no;
        #INET_RAW_DIAG=no;
        #TCP_CONG_BIC=no;
        #TCP_CONG_WESTWOOD=no;
        #TCP_CONG_HTCP=no;
        #TCP_CONG_HSTCP=no;
        #TCP_CONG_HYBLA=no;
        #TCP_CONG_VEGAS=no;
        #TCP_CONG_NV=no;
        #TCP_CONG_SCALABLE=no;
        #TCP_CONG_LP=no;
        #TCP_CONG_VENO=no;
        #TCP_CONG_YEAH=no;
        #TCP_CONG_ILLINOIS=no;
        #TCP_CONG_DCTCP=no;
        #TCP_CONG_CDG=no;
        #TCP_CONG_BBR=no;
    ...
    }

Unfortunately setting all of them of no was risky because there are a few modules that I couldn’t match to any config and make the system not boot.

To figure outI tried using the web, https://www.kernelconfig.io/index.html and searches but some of them where too hard to guess.

Trying local LLMs

TO solve this while experimenting with LLMs to refine my opinions and get some heat in the room, I decided to try my local ollama to ask for the module. From all the models that I can run, only qwen3.6 had some information within its weights.

I tried at first to ask to run lsmod and give me all the matches, but there is no way the model can do that at all, it loses context very quickly.

Then I ran a pydantic-ai script to get one answer every ~10 minutes:


kernel_modules = [
        "af_packet", ...]

agent = Agent(  # (1)
          "ollama:qwen3.6:35b-a3b",
          model_settings={"thinking":False},
          deps_type=str,
          instructions=(
      "return the linux kernel configuration option that generates the module that the user provides. Return only the string that starts with CONFIG_"),
)


for kernel_module in kernel_modules:
    print(kernel_module)
    result = agent.run_sync(f'The module is {kernel_module}', conversation_id='new')  # (3)
    print(result.output)
    print("--------------------")

The thing is that it matched modules that were easy. Any module that wasn’t immediately obvious would not get right and give a false answer. It is considerably worse than me searching manually. It was building up ram usage too, around 300mb per question.

Instead I asked for some grep to get the module from the kernel sources. I got a few options:

  1. Grep Kbuild Makefiles (Most Direct)
  2. Grep Kconfig for tristate
  3. greping modname.h
  4. Using module markers
  5. Using python kconfiglib

The thing is that 1 and 3 seemed to be the easiest and more reliable options (2 and 4 did not work for me, and I didn’t try 5). Both of them I already knew and I used a faster searcher than grep anyway.

Lastly, running make menuconfig in the linux kernel source directory and using the search inside was an even better option as I had help and visibility of the path.

Removing modules slowly

Despite being almost certain that I have all the modules that I need declared as modules and everything else declared as no, I am slowly adding some of these declarations to configuration.nix.

This is because by setting some options to “no”, other options become an error and need to be “unset”. There is also the possibility that I remove something that I shouldn’t so I want to change it gradually and notice when it happens.