Update 2018-01-02: See my post on Rapido for a more automated approach to the procedure outlined below.
Inspired by Stefan Hajnoczi's excellent blog post, I recently set about constructing an environment for rapid testing of Linux kernel changes, particularly focused on the LIO iSCSI target. Such an environment would help me in number of ways:
- Faster dev / test turnaround.
- A modified kernel can be compiled and booted in a matter of seconds.
- Improved resource utilisation.
- No need to boot external test hosts or heavyweight VMs.
- Simplified and speedier debugging.
My requirements were slightly different to Stefan's, in that:
- I'd prefer to be lazy and use Dracut for initramfs generation.
- I need a working network connection between VM and hypervisor system
- The VM will act as the iSCSI target, the hypervisor as the initiator.
Starting with the Linux kernel, the first step is to build a bzimage:
~/> git clone \ git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
hack, hack, hack.
~/linux/> make menuconfig
Set CONFIG_IP_PNP_DHCP=y and CONFIG_E1000=y to enable IP address assignment on boot.
~/linux/> make -j6 ~/linux/> make modules ~/linux/> INSTALL_MOD_PATH=./mods make modules_install ~/linux/> sudo ln -s $PWD/mods/lib/modules/$(make kernelrelease) \ /lib/modules/$(make kernelrelease)This leaves us with a compressed kernel image file at arch/x86/boot/bzimage, and corresponding modules installed under mods/lib/module/$(make kernelrelease), where $(make kernelrelease) evaluates to 4.1.0-rc7+ in this example. The /lib/modules/4.1.0-rc7+ symlink allows Dracut to locate the modules.
The next step is to generate an initial RAM filesystem, or initramfs, which includes a minimal set of user-space utilities, and kernel modules needed for testing:
~/linux/> dracut --kver "$(make kernelrelease)" \ --add-drivers "iscsi_target_mod target_core_mod" \ --add-drivers "target_core_file target_core_iblock" \ --add-drivers "configfs" \ --install "ps grep netstat" \ --no-hostonly --no-hostonly-cmdline \ --modules "bash base shutdown network ifcfg" initramfs ... *** Creating image file done ***
We now have an initramfs file in the current directory, with the following contents:
- LIO kernel modules obtained from /lib/module/4.1.0-rc7, as directed via the --kver and --add-drivers parameters.
- User-space shell, boot and network helpers, as directed via the --modules parameter.
We're now ready to use QEMU/KVM to boot our test kernel and initramfs:
~/linux/> qemu-kvm -kernel arch/x86/boot/bzImage \ -initrd initramfs \ -device e1000,netdev=network0 \ -netdev user,id=network0 \ -redir tcp:51550::3260 \ -append "ip=dhcp rd.shell=1 console=ttyS0" \ -nographic
This boots the test environment, with the kernel and initramfs previously generated:
[ 3.216596] dracut Warning: dracut: FATAL: No or empty root= argument [ 3.217998] dracut Warning: dracut: Refusing to continue ... Dropping to debug shell. dracut:/#
From the dracut shell, confirm that the QEMU DHCP server assigned the VM an IP address:
dracut:/# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default ...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 ... inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
Port 3260 (iSCSI) on this interface is forwarded to/from port 51550 on the hypervisor, as configured via the qemu-kvm -redir parameter.
Now onto LIO iSCSI target setup. First off load the appropriate kernel modules:
dracut:/# modprobe iscsi_target_mod dracut:/# cat /proc/modules iscsi_target_mod 246669 0 - Live 0xffffffffa006a000 target_core_mod 289004 1 iscsi_target_mod, Live 0xffffffffa000b000 configfs 22407 3 iscsi_target_mod,target_core_mod, Live 0xffffffffa0000000
LIO configuration requires a mounted configfs filesystem:
dracut:/# mount -t configfs configfs /sys/kernel/config/ dracut:/# cat /sys/kernel/config/target/version Target Engine Core ConfigFS Infrastructure v4.1.0 on Linux/x86_64 on 4.1.0-rc1+
An iSCSI target can be provisioned by manipulating corresponding configfs entries. I used the lio_dump output on an existing setup as reference:
dracut:/# mkdir /sys/kernel/config/target/iscsi
dracut:/# echo -n 0 > /sys/kernel/config/target/iscsi/discovery_auth/enforce_discovery_auth dracut:/# mkdir -p /sys/kernel/config/target/iscsi/<iscsi_iqn>/tpgt_1/np/10.0.2.15:3260 ...
Finally, we're ready to connect to the LIO target using the local hypervisor port that forwards to the VM's virtual network adapter:
~/linux/> iscsiadm --mode discovery \ --type sendtargets \ --portal 127.0.0.1:51550 10.0.2.15:3260,1 iqn.2015-04.suse.arch:5eca2313-028d-435c-9131-53a5ab256a83
It works!
There are a few things that can be adjusted:
- Port forwarding to the VM network is a bit fiddly - I'm now using a bridge/TAP configuration instead.
- When dropping into the emergency boot shell, Dracut executes scripts carried under /lib/dracut/hooks/emergency/. This means that a custom script can be triggered on boot via:
~/linux/> dracut -i runme.sh /lib/dracut/hooks/emergency/02-runme.sh ...
- It should be possible to have Dracut pull the kernel modules in from the
temporary directory, but I wasn't able to get this working:
~/linux/> INSTALL_MOD_PATH=./mods make modules_install ~/linux/> dracut --kver "$(make kernelrelease)" --kmoddir ./mods/lib/...
- Boot time and initramfs file IO performance can be improved by disabling compression. This is done by specifying the --no-compress Dracut parameter.
Update 20150722:
- Don't install kernel modules as root, set up a /lib/modules symlink for Dracut instead.
- Link to bridge/TAP networking post.
- Describe boot script usage.
- Use $(make kernelrelease) rather than a hard-coded 4.1.0-rc7+ kernel version string - thanks Aurélien!
Update 20180102: Link to the Rapido project.
Thank you for your guide, it really helped me!
ReplyDeleteI'm also having success with this Buildroot setup: https://github.com/cirosantilli/linux-kernel-module-cheat
ReplyDelete