Part of the reason I’ve started this blog is so I can write down my tech notes with important details instead of forgetting the details and having to spend a couple of hours figuring things out again when I dust off a project I put on the backburner months ago.
I apologize if you came here looking for a complete guide to net booting a Pi; I only intended to write this blog to fill in a missing detail that bugs me. Here’s a couple of places that might serve as good starting points if that’s what you need:
https://tp4348.medium.com/netboot-raspberry-pi-using-ubuntu-20-04-os-cb3973ff65b0
Anyway, to the point: I got PXE boot to work. Everything boots up, I can ssh to the Pi, etc. But as someone who has deployed NFS to clients before, I noticed root is mounted as NFSv3:
# mount | grep nfs
192.168.29.2:/pxe/storepi on / type nfs (rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,nolock,proto=tcp,port=2049,timeo=600,retrans=10,sec=sys,local_lock=all,addr=192.168.29.2)
Is this really a big deal? Not really. But NFSv4 has a lot of improvements over NFSv3, including features that make the protocol more efficient. You probably won’t notice it unless you’re benchmarking. But I think it’s important to be exposed to the latest technologies so I can be familiar and apply what I’ve learned elsewhere. So thus, I *demand* that this be mounted as NFS v4.
Why is this happening? Linux will normally mount things with the highest possible NFS version that both client and server support. The client and sever both run Ubuntu 22.04 and should happily allow a NFSv4.2 mount.
The cause is found in the initramfs environment. The initramfs contains a very small working system, i.e. a few utilities that are just enough to prepare and boot into the *real* system. This includes filesystem mounting tools.
The “nfsmount” utility that ships in Debian (and is sourced by Ubuntu) does not support NFSv4. There are some ugly hacks and attempts to make it work, if you care to read the comments.
https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/1954716
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=409272
I found an easier solution: copy the /sbin/mount.nfs utility into the initramfs and call that instead of the weak-sauce “nfsmount” utility that comes by default.
What are the downsides to doing this, and why doesn’t Debian / Ubuntu just do that? Probably because the utilities that are in initramfs are intended to be *very small*. They do not even use glibc, they use klibc which is intended to produce tiny binaries.
Is there an actual downside to having a larger-than-normal initramfs? In this scenario I don’t see it but I’m guessing Debian and Ubuntu don’t want to do that due to the bloating of the initramfs.
Here are the steps:
- Do the necessary steps to get TFTP / NFS booting of your Ubuntu / Raspbian system working. Make sure it actually works and boots successfully before proceeding further.
- Create a “/usr/share/initramfs-tools/hooks/nfs” file with the contents shown in the code blob below. Make sure it is executable.
- Edit the existing file “/usr/share/initramfs-tools/scripts/nfs”. Refer to the code blob below.
In the function “nfs_mount_root_impl()”, comment out the “nfsmount” command and add the “mount.nfs” command that I have shown. Those are the only changes you need to make here.
Note that I snipped out all but the last few lines of the function. Don’t actually snip out all of this in your file, I did that just to keep the code blob tidy. You still need all of that for this to work correctly. - Regenerate your initramfs using this command:
update-initramfs -c -k all - Reboot. If you did this correctly, the system should come up and you should see a similar output from the “mount” command as per the below code blob showing an nfs vers=4.x mount.
root@storepi:/usr/share/initramfs-tools/hooks# cat nfs
#!/bin/sh
set -e
PREREQ=""
prereqs () {
echo "${PREREQ}"
}
case "${1}" in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_exec /sbin/mount.nfs /sbin
exit 0
from /usr/share/initramfs-tools/scripts/nfs
# parse nfs bootargs and mount nfs
nfs_mount_root_impl()
{
.. snipped ..
# shellcheck disable=SC2086
mount.nfs ${NFSROOT} ${rootmnt} -o nolock ${roflag} ${NFSOPTS}
#nfsmount -o nolock ${roflag} ${NFSOPTS} "${NFSROOT}" "${rootmnt?}"
}
192.168.29.2:/pxe/storepi on / type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=10,sec=sys,clientaddr=192.168.29.16,local_lock=none,addr=192.168.29.2)
After following this exactly, I’m experiencing an issue:
/init: 75 mount.nfs not found
I modified the script to call /sbin/mount.nfs and no change.
I’ve also used unmkinitramfs to examine the initrd.img and found mount.nfs in the correct path (/sbin/mount.nfs)
Any ideas where this is going sideways?
Correction to the above… exact message:
/init: line 75: mount.nfs: not found
For anyone interested in how much the initrd will be “bloated”:
10MB with nfsmount
80MB with mount.nfs
It works!
This is my cmdline.txt
… … root=/dev/nfs rootfstype=nfs4 nfsroot=xxx.xxx.xxx.xxx:/path/to/nfs,vers=4.2 rw … …
Thank you!