Kernel development in Emacs

A quick rundown of how I do Linux kernel development in Emacs.

I have an iMac as my main desktop computer and I am a heavy Emacs user. I do a lot of work with the Linux kernel which I do remotely on a Linux PC and I use Eglot with the clangd language server over TRAMP. Here is an overview of my setup.

My desktop

My desktop computer is a 27" Intel iMac running macOS Sonoma (14.7) which I use for the majority of tasks.

I use Emacs as my main editor / development environment, whether it be for C, Go, Python, Raku, shell, yaml, json, or any number of other languages and file types that Emacs has a mode for.

I lean heavily on language servers when they are available and use Eglot, the Emacs built-in LSP client.

My Linux host

I used to do Linux hosted development in a VM running on my mac and sometimes this is still desirable, e.g. when I am on the road with my laptop. But performance really sucked for large projects like the Linux kernel so I have a 12 core AMD Ryzen host that I use as a Linux development server.

My Linux development server currently runs Fedora 41 and it has clangd installed from the clang-tools-extra package.

Building Linux

A prerequisite for running clangd in the kernel project is to first prepare a compile_commands.json file. There is a target in the kernel Makefile for this and I typically include it in every build command:

time make -j 24 bzImage modules compile_commands.json

Editing files

Emacs runs on my desktop mac and I open files remotely on the Linux host using the Emacs built-in transparent remote access (TRAMP). For example, here I remotely open net/core/rtnetlink.c in my net-next git worktree:

C-x C-f /ssh:linux-host:net-next/net/core/rtnetlink.c

Once I have a kernel project file open in Emacs, I can use Eglot to start the clangd language server:

M-x eglot

The Emacs modeline now shows a status indicator for Eglot: [eglot:net-next:5%]. When indexing is complete, the progress percentage is no longer displayed.

A screenshot of Emacs showing a kernel source file, with eglot enabled
Linux kernel source file with Eglot enabled

These are the most common navigation commands (when using the default key bindings):

  • M-. – find definitions (go to definition if there is only one candidate)
  • M-, – go back
  • M-? – find references

Emacs configuration

There are a few essential C mode configuration settings for working on the Linux kernel project, and I always want to run Eglot when I open kernel files. I have this configuration in my .emacs file:

(add-hook 'c-mode-common-hook
          (lambda ()
            (setq c-basic-offset 8)
            (setq indent-tabs-mode t)
            (setq tab-width 8)
            (eglot-ensure)))

Preparing patches

When it comes to preparing a patch series for sending upstream, Magit is indispensable. It has great UX for interactively staging individual hunks and for preparing commit messages.

A screenshot of Emacs displaying the Magit status buffer with a hunk selected for staging
Hunk selection in Magit

When a patch series needs to be refactored, interactive rebase is available within Magit. This makes it a breeze for ordering, editing, squashing or whatever else is needed to modify the patches.

A screenshot of Emacs displaying the Magit interactive rebase editor showing several commits and editing instructions
The git-rebase-todo buffer

Magit is incredibly powerful and I cannot do it justice here. Have a read of this Magit walk through to learn more.

Formatting and sending patches

When I am ready to generate a patch set for sending upstream, I use the git format-patch command like this:

git format-patch -v1 --cover-letter --subject-prefix "PATCH net-next" "HEAD~10"

This creates a series of patch files, with a cover letter, named v1-0000-cover-letter.patch, and patches named v1-0001-..., etc. The subject line of each email message has a prefix that declares it is a patch targeting the net-next tree.

I check that the patch series meets submission guidelines by running checkpatch.pl:

./scripts/checkpatch.pl v1-00*
-------------------------------------------------------------------------------
/net/imac/Users/donaldh/git/kernel-patches/ynl/wiphy/v1-0000-cover-letter.patch
-------------------------------------------------------------------------------
total: 0 errors, 0 warnings, 0 lines checked

/net/imac/Users/donaldh/git/kernel-patches/ynl/wiphy/v1-0000-cover-letter.patch has no obvious style problems and is ready for submission.
--------------------------------------------------------------------------------------------------------------------
/net/imac/Users/donaldh/git/kernel-patches/ynl/wiphy/v1-0001-tools-net-ynl-remove-extraneous-plural-from-varia.patch
--------------------------------------------------------------------------------------------------------------------
total: 0 errors, 0 warnings, 21 lines checked

/net/imac/Users/donaldh/git/kernel-patches/ynl/wiphy/v1-0001-tools-net-ynl-remove-extraneous-plural-from-varia.patch has no obvious style problems and is ready for submission.
...

The get_maintainer.pl script is used to identify the maintainers, reviewers and relevant mailing lists that should be recipients of the patch series when it gets sent.

./scripts/get_maintainer.pl v1-00*

I then use git send-email to send the patch series to the maintainers, reviewers and mailing lists.

Expectations for contributions to the kernel networking subsystem can be found at https://docs.kernel.org/process/maintainer-netdev.html