Add blog posts
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
193
content/posts/custom-linux-kernel/index.md
Normal file
193
content/posts/custom-linux-kernel/index.md
Normal file
@@ -0,0 +1,193 @@
|
||||
+++
|
||||
title = "Compiling a Custom Arch Linux Kernel"
|
||||
date = 2021-12-31T17:29:00Z
|
||||
draft = false
|
||||
summary = "Last week, I finally gave in and tried my luck by compiling a custom Linux kernel. After several days of work and a lot of hair-pulling..."
|
||||
tags = ["Guide", "Linux"]
|
||||
|
||||
aliases = ['/custom-linux-kernel']
|
||||
|
||||
[hero]
|
||||
src = "hero.jpg"
|
||||
caption = 'Photo by <a href="https://unsplash.com/@yancymin">Yancy Min</a> via <a href="https://unsplash.com">Unsplash</a>'
|
||||
+++
|
||||
|
||||
> A Linux user, a vegan, and an atheist walk into a bar. I know because they told everyone.
|
||||
|
||||
During these past few months, I have been switching between various kernels depending on the work I wanted to do. Sometimes I needed an [IOMMU patch](https://aur.archlinux.org/packages/linux-vfio/), some other times I wanted to try some Linux [gaming](https://github.com/zen-kernel/zen-kernel), and, more often than not, I was too slow to select the correct GRUB entry and ended up using the [default](https://github.com/archlinux/linux) Arch Linux kernel.
|
||||
|
||||
Last week, I finally gave in and tried my luck by compiling a custom kernel tailored specifically for my [new laptop]({{% ref "/posts/lenovo-legion-7" %}}). After several days of work and a lot of hair-pulling, I managed to boot into a kernel which only had support for the exact hardware I needed.
|
||||
|
||||
```bash
|
||||
$ cat boot.txt
|
||||
Startup finished in 4.793s (firmware) + 1.253s (loader) + 1.408s (kernel) + 3.794s (userspace) = 11.251s
|
||||
graphical.target reached after 3.794s in userspace
|
||||
$ cat boot-eirene.txt
|
||||
Startup finished in 4.781s (firmware) + 1.264s (loader) + 1.138s (kernel) + 3.365s (userspace) = 10.551s
|
||||
graphical.target reached after 3.365s in userspace
|
||||
```
|
||||
|
||||
Besides the slightly improved boot times, I also saw a surprising increase in battery life (7.9 Watts used when idle compared to 11) and about 500MBs more free RAM.
|
||||
|
||||
In this post, I will be explaining the main steps I followed when creating this kernel, hopefully helping anyone that finds themselves in a similar situation. You can find the finished configuration in this project's [Git repo](https://git.karaolidis.com/karaolidis/linux-eirene).
|
||||
|
||||
## Preparation
|
||||
|
||||
> [!warning] Disclaimer
|
||||
> I am not an expert and I don't claim to be one. I'm just a CompSci student who enjoys tinkering with his system and trying to optimize it as much as possible while sacrificing my sanity in the process. I am not responsible if you break your system or lose important files.
|
||||
|
||||
Before you begin, I recommend installing a normal Linux distribution with good support for your hardware and making sure that everything is working how it's supposed to. In my case, I used Arch Linux with the latest kernel.
|
||||
|
||||
Once you confirm that everything is working, you should also store the output of some commands so that you have a working reference point when booting with your custom kernel:
|
||||
|
||||
- `lspci -nnkkvvv` for verbose hardware and driver details
|
||||
- `lsmod` for loaded kernel modules
|
||||
- `lsusb` for connected devices on various USB busses
|
||||
- `lscpu` for CPU information
|
||||
|
||||
You should also have some clear goals before you start working on your kernel's configuration. I originally made the mistake of going in blind, which made me waste several hours debugging boot errors, changing previously-set options, and eventually forcing me to restart from scratch.
|
||||
|
||||
### Installing build dependencies
|
||||
|
||||
Once you are ready to start, install the packages you need for building the kernel. If you are using Arch, this can be easily done by running:
|
||||
|
||||
```bash
|
||||
$ pacman -S base-devel xmlto kmod inetutils bc libelf git cpio perl tar xz
|
||||
```
|
||||
|
||||
### Acquiring the source code
|
||||
|
||||
After installation is complete, go ahead and download the latest kernel source code. You can do that by either going to https://www.kernel.org/, grabbing the latest tarball, and unpacking it, or by using git and pulling from the official mirror:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/torvalds/linux
|
||||
```
|
||||
|
||||
You should also make sure that the kernel tree is clean by running `make mrproper` in the downloaded directory.
|
||||
|
||||
## Configuration
|
||||
|
||||
Now it's finally time to configure the kernel. What I recommend doing is getting your distribution's default `.config` by running `zcat /proc/config.gz > .config`, and [compiling]({{% ref "/posts/custom-linux-kernel#compilation" %}}) it to act as a sanity check for further `.config` changes.
|
||||
|
||||
### Advanced configuration
|
||||
|
||||
Once you've made sure that the default config is working, you can go ahead and run `make clean` to remove the previous build and `make nconfig` to open the configuration menu.
|
||||
|
||||
The sheer number of choices that will appear in front of you will be overwhelming, but don't let that discourage you. If you use a slow and methodic approach you'll realize that while compiling a custom kernel is time-consuming, it is certainly not hard.
|
||||
|
||||
Just a word of advice: Do not try and configure everything in one run. There is a high chance that you will break something and spend more time debugging it than if you were to make small, gradual changes, recompiling every once in a while to check if everything is working.
|
||||
|
||||
When I was creating my config, I had 3 main goals in mind:
|
||||
|
||||
- Keep it as minimal as possible
|
||||
- Improve performance as much as possible without harming battery life
|
||||
- Disable any logging / debugging capabilities that might increase runtime overhead
|
||||
|
||||
I'm not going to go through every choice I made since most of the options are architecture-dependent and analyzing all use-cases would take months. Even if I did, if you copied my config without understanding the functionality behind each feature, you would most likely end up with an unbootable system.
|
||||
|
||||
There are a couple of resources you can use to learn what each option does:
|
||||
|
||||
- The in-built help text, available by pressing `h`
|
||||
- [DOTSLASHLINUX](https://firasuke.github.io/DOTSLASHLINUX/post/the-linux-kernel-configuration-guide-part-1-introduction/)'s kernel configuration guide
|
||||
- The [Gentoo Wiki](https://wiki.gentoo.org/wiki/Kernel/Gentoo_Kernel_Configuration_Guide)
|
||||
- Good ol' Google
|
||||
|
||||
The general strategy I followed when working on this project was to first get the easy things out of the way: These included options like CPU architecture support, compression, filesystems, etc. I then worked my way through one submenu at a time, recompiling the kernel to make sure I didn't break anything. I recommend leaving the behemoth that is the Device Drivers menu for last since that will take the most time and contains multiple options dependent on other settings.
|
||||
|
||||
### Applying Patches
|
||||
|
||||
Now would also be a good time to apply any needed patches. In my case, I wanted to apply an [ACS Override](https://aur.archlinux.org/cgit/aur.git/tree/?h=linux-vfio) patch so that I could have more control over my IOMMU Groups. You can easily apply .patch files by moving them to the kernel source directory and running:
|
||||
|
||||
```bash
|
||||
$ patch -Np1 < [name-of-patch]
|
||||
```
|
||||
|
||||
If you don't get any rejects, you are good to go for compilation.
|
||||
|
||||
## Compilation
|
||||
|
||||
Now that you've made some changes to your config, it's time to compile it. Go ahead and run the following commands:
|
||||
|
||||
```bash
|
||||
$ make
|
||||
$ make modules
|
||||
$ sudo make modules_install
|
||||
```
|
||||
|
||||
Each step should take a decent amount of time, depending on how large the kernel is. In my case, it took about 10 minutes on a Ryzen 7 5800H. If everything went well, you should notice a new `arch/x86/boot/bzImage` file.
|
||||
|
||||
## Installation
|
||||
|
||||
Now that you have finished compiling your kernel, it is finally time to install it.
|
||||
|
||||
### Kernel
|
||||
|
||||
Go ahead and run the following command. You can name the resulting file as you wish, provided that it is prefixed with `vmlinuz`. In my case, I named it `vmlinuz-linux-eirene`:
|
||||
|
||||
```bash
|
||||
$ cp -v arch/x86/boot/bzImage /boot/vmlinuz-linux-eirene
|
||||
```
|
||||
|
||||
### Initial RAM disk
|
||||
|
||||
Once you have copied your kernel to the `/boot` directory, you should also generate an initial RAM disk. You can easily do that by copying the default kernel's preset:
|
||||
|
||||
```bash
|
||||
$ cp /etc/mkinitcpio.d/linux.preset /etc/mkinitcpio.d/linux-eirene.preset
|
||||
```
|
||||
|
||||
After that, edit the new file and make sure to change it so it matches the name you selected.
|
||||
|
||||
```ini hl_lines=[1, 4, 9, 13]
|
||||
# mkinitcpio preset file for the 'linux-eirene' package
|
||||
|
||||
ALL_config="/etc/mkinitcpio.conf"
|
||||
ALL_kver="/boot/vmlinuz-linux-eirene"
|
||||
|
||||
PRESETS=('default' 'fallback')
|
||||
|
||||
#default_config="/etc/mkinitcpio.conf"
|
||||
default_image="/boot/initramfs-linux-eirene.img"
|
||||
#default_options=""
|
||||
|
||||
#fallback_config="/etc/mkinitcpio.conf"
|
||||
fallback_image="/boot/initramfs-linux-eirene-fallback.img"
|
||||
fallback_options="-S autodetect"
|
||||
```
|
||||
|
||||
### Bootloader
|
||||
|
||||
Finally, add an entry for your new kernel in your bootloader's configuration file. If you are using GRUB, you can simply run the following command:
|
||||
|
||||
```bash
|
||||
$ grub-mkconfig -o /boot/grub/grub.cfg
|
||||
```
|
||||
|
||||
If you didn't get any errors, you should be able to select the newly-compiled kernel the next time you boot your computer!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
One easy way to check for errors is to look at your system's journal:
|
||||
|
||||
```bash
|
||||
$ journalctl -p 3 -b
|
||||
Dec 31 17:44:50 eirene kernel: Spectre V2 : Spectre mitigation: kernel not compiled with retpoline; no mitigation available!
|
||||
Dec 31 17:44:50 eirene kernel: ACPI BIOS Error (bug): Could not resolve symbol [\_SB.PCI0.PB2], AE_NOT_FOUND (20210930/dswl>
|
||||
Dec 31 17:44:50 eirene kernel: ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20210930/psobject-220)
|
||||
Dec 31 17:44:52 eirene systemd-modules-load[318]: Failed to find module 'nvidia'
|
||||
Dec 31 17:44:52 eirene systemd-modules-load[318]: Failed to find module 'nvidia-uvm'
|
||||
Dec 31 17:44:52 eirene systemd-backlight[718]: Failed to get backlight or LED device 'backlight:acpi_video0': No such device
|
||||
Dec 31 17:44:52 eirene systemd[1]: Failed to start Load/Save Screen Backlight Brightness of backlight:acpi_video0.
|
||||
Dec 31 17:44:53 eirene kernel: Bluetooth: hci0: Failed to read codec capabilities (-56)
|
||||
Dec 31 17:46:08 eirene lightdm[1491]: gkr-pam: unable to locate daemon control file
|
||||
```
|
||||
|
||||
Keep in mind that not everything shown here is important, but it can be a good lead for finding the root cause of an issue. In the worst-case scenario, you can simply backtrack and try recently-changed config options one by one to see which one creates the problem.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Even though configuring a kernel is challenging, the feeling of booting it for the first time and seeing the resulting performance/battery life increase is definitely satisfying.
|
||||
|
||||
Maintaining that kernel is an entirely different story, but that can be fun as well, depending on how much you hate yourself, your free time, and your mental health :grinning:.
|
||||
|
||||
Happy coding and happy new year!
|
Reference in New Issue
Block a user