8.6 KiB
layout | title | date | permalink | categories | author | published |
---|---|---|---|---|---|---|
post | My first contribution to Linux Kernel: Step by step | 2021-11-06 00:00:00 | first-linux-contribution/ | programming | Mahdi | true |
This post is under construction. I am in the process of trying to contribute to the Linux Kernel. This post is not finished and will get updated as I go
I use a MacBook Pro (mid-2014) with macOS, so I need to have a virtual machine for running a linux system with my kernel. I will also be doing the coding on this linux virtual machine as building the kernel is easier in a linux system than macOS.
Setting up the Virtual Machine (Archlinux)
I create a virtual machine with Archlinux on my macOS using QEMU:
- Download the Archlinux iso image
- Create a qemu disk {% highlight bash %} qemu-img create disk.img 15G {% endhighlight %}
- Start the machine and install Archlinux {% highlight bash %} qemu-system-x86_64 -cdrom archlinux-2021.11.01-x86_64.iso -boot order=d -drive format=raw,file=disk.img -m 8G {% endhighlight %}
- Start the machine after installing (note I forward 2222 to 22 so I can SSH/SCP to the virtual machine. I also set 4 CPUs so I can use threads for faster builds in the VM) {% highlight bash %} qemu-system-x86_64 -boot -drive format=raw,file=disk.img -m 8G -smp cpus=4 -net user,hostfwd=tcp::2222-:22 -net nic {% endhighlight %}
- Install dependencies for building the kernel {% highlight bash %} pacman -S gcc git make {% endhighlight %}
- Clone linux (note that you may want to clone another repository other than the stable if you are working on a specific part. For example I had to clone the
netdev
group's tree). List of different trees. {% highlight bash %} git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
netdev group
git clone git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
{% endhighlight %}
7. Install the necessary dependencies for building the kernel
{% highlight bash %}
pacman -S flex base-devel xmlto kmod inetutils bc libelf git cpio perl tar xz
{% endhighlight %}
8. Copy configuration of archlinux (optional: also use modprobed-db to remove unnecessary modules)
{% highlight bash %}
zcat /proc/config.gz > .config
{% endhighlight %}
9. Make sure you enable debugging configurations
{% highlight bash %}
CONFIG_FRAME_POINTER=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_DEBUG_INFO=y
{% endhighlight %}
10. Make! The -j8
parameter specifies the number of threads to be used by the build. My CPU has 8 threads and so I use it all.
{% highlight bash %}
make -j8
{% endhighlight %}
11. Install the newly built Kernel. I create this as a script file and run it after every build from the root of repository.
{% highlight bash %}
make -j8 modules_install
RELEASE=$(cat include/config/kernel.release)
cp -v arch/x86_64/boot/bzImage /boot/vmlinuz-linux${RELEASE}
mkinitcpio -k $RELEASE -g /boot/initramfs-linux${RELEASE}.img
mkinitcpio -k $RELEASE -s autodetect -g /boot/initramfs-linux-fallback${RELEASE}.img
grub-mkconfig -o /boot/grub/grub.cfg
{% endhighlight %}
- Reboot and choose the new kernel (might be under "Advanced" in the bootloader)
Development Environment
Setup your environment for development. Mine consists of setting up tmux so I can have multiple terminals and neovim.
In the guest machine: {% highlight bash %} pacman -S neovim openssh tmux echo ' -z "$TMUX" && exec tmux' >> /etc/profile
also follow https://github.com/junegunn/vim-plug for Neovim
{% endhighlight %}
And in the host: {% highlight bash %} scp -P 2222 ~/.tmux.conf root@localhost:/root scp -r -P 2222 ~/.config/nvim root@localhost:/root/.config/ {% endhighlight %}
One thing I found necessary, due to limited storage, is a script to cleanup each linux version after I'm done with them, since they create a couple of files in different places. I call this cleanup-linux.sh
:
{% highlight bash %} VERSION=$1 rm /boot/vmlinuz-linux${VERSION} rm /boot/initramfs-linux${VERSION}.img rm /boot/initramfs-linux-fallback${VERSION}.img rm -r /usr/lib/modules/${VERSION} {% endhighlight %}
Debugging
There is a pr_debug
function used over the code, in order to enable those logs in dmesg
for a specific module, you can do this:
{% highlight bash %} echo 8 > /proc/sys/kernel/printk echo 'module ip_set +p' > /sys/kernel/debug/dynamic_debug/control {% endhighlight %}
Note that, this works if you have dynamic debug enabled in your .config
:
{% highlight bash %}
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DYNAMIC_DEBUG_CORE=y
{% endhighlight %}
You can then look at dmesg
while running the code to see those logs:
{% highlight bash %}
dmesg
{% endhighlight %}
Kernel Oops, Bug and Panic
If you get a Kernel Oops, Kernel Bug or similar, here are some good resources on how to read and understand the output:
- Bug Hunting
- Understanding a Kernel Oops!
- How to read, understand, analyze and debug a linux kernel panic
- Kernel Debugging
Reading The Call Trace
For example, I wanted to be able to understand the call trace of this Kernel Bug: bug-207773
The call trace section starts with:
[226832.533889] Call Trace:
[226832.534377] <IRQ>
[226832.534776] recent_entry_update+0x52/0xa0 [xt_recent]
[226832.535690] recent_mt+0x167/0x328 [xt_recent]
[226832.536488] ? set_match_v4+0x96/0xb0 [xt_set]
[226832.537407] ipt_do_table+0x24f/0x610 [ip_tables]
[226832.538277] ? ipt_do_table+0x33e/0x610 [ip_tables]
[226832.539146] ? l4proto_manip_pkt+0xde/0x440 [nf_nat]
[226832.540049] ? ip_route_input_rcu+0x40/0x280
[226832.540831] nf_hook_slow+0x40/0xb0
[226832.541477] ip_forward+0x424/0x450
[226832.542116] ? ip_defrag.cold+0x37/0x37
[226832.542814] ip_rcv+0x9c/0xb0
The way I did it was to run gdb
on the vmlinux
file in the root of the repository after build, and then load the symbol files of each module that is relevant:
{% highlight gdb %} gdb vmlinux
(gdb) add-symbol-file vmlinux.o (gdb) add-symbol-file net/ipv4/netfilter/ip_tables.o (gdb) list *(ipt_do_table+0x24f) (gdb) list *(nf_hook_slow+0x40) {% endhighlight %}
Creating your patch
Here are some good guidelines on how to prepare and send your patch:
There might be a file called MAINTAINERS
in the root of the repository you cloned, it may include some notes and tips on how to make the life of maintainers easier by following some guidelines, read it!
What did I work on?
The first issue I was interested in turned out to be an invalid bug: I found that out by investigating the script the user was testing and measuring how much time each part of the script took to find out the main culprit: bug-214851. But I learned a lot during this alone, mostly about how to build things quickly, where to look for modules, how to enable debugging for them, etc.
I wanted to work on the network modules, so I looked around there.
In order to understand how networking module of linux works, I read these resources: