This is the technical, optional upgrade that will enable you to boot your OS from an external SSD.
Syonyk has an awesome Jetson Nano guide with all of this stuff explained and was the primary source of info while researching how to do this.
JetsonHacks also have a guide that should make this much easier to do, as they’ve prepared scripts that do all of the work for you, but I haven’t tried it so I can’t say it works for sure, but it should!
This will take some time. Taking kernels apart, patching them and building them takes a while even on your regular workstations or servers, but doing it on an embedded device is a whole new world of pain when it comes to waiting for stuff to build, compile or extract. Be prepared to be patient while you’re doing this.
We want to use our external SSD as the root filesystem because of the huge performance boost it gives us. But the problem is this:
So, how can we work this out? Simple: patch the kernel and embed the firmware we need right into it, so it doesn’t need to read it from the root filesystem, which we can then freely put on our external SSD.
While we’re at it, there’s another thing that would give us a noticeable performance boost which we could build into the kernel, same-filled page merging support for zswap.
All of the steps written below are meant to be run directly on your Jetson Nano.
First of all, since the Nano doesn’t run the stock Linux kernel, we’ll need the sources for its custom kernel. Miraculously Nvidia actually provides it.
Steps shown below:
cd Downloads
tar -xf public_sources.tbz2
cd ~
tar -xf ~/Downloads/public_sources/kernel_src.tbz2
cd ~/kernel/kernel-4.9
Once we’re in the kernel directory, we’ll save the current kernel configuration from the Nano to it:
zcat /proc/config.gz > .config
Now we need to copy the USB firmware from the root filesystem into the kernel directory, since we want to pack it together with the kernel:
cp /lib/firmware/tegra21x_xusb_firmware ./firmware/
Now we need to change the kernel config, and since we’re using a GUI desktop environment on the Nano, we can just use menuconfig:
sudo apt-get install libncurses5-dev
make menuconfig
You should see something like this:
When selecting kernel features in menuconfig, * denotes a built-in feature and M denotes a module. You’ll be wanting stars for the features you want to build in.
We didn’t select the 3x allocator (z3fold) because isn't reliable prior to mid-4.20 kernel versions. With same-filled page merging, the results are about the same as the zbud 2x allocator.
Now we’ll update ZSwap to enable same-filled page compression. The kernel that the Jetson Nano is running at the time of writing is 4.9, which doesn’t include it, but it can easily be backported from a newer kernel.
One of the best things about Linux, what makes it what it is, is that it’s available for everyone, and we can find it on Linus Torvalds’ GitHub:
On GitHub, you can add .patch at the end of the URL to get the patch file for the commit you’re looking at. Just be sure to remove the #diff parameter from the URL if you’re looking at the diff from the commit.
See instructions below:
Make sure you’re in the kernel-4.9 directory before proceeding.
# You should be in the Downloads directory
wget https://github.com/torvalds/linux/commit/a85f878b443f8d2b91ba76f09da21ac0af22e07f.patch
# Change to the kernel directory
cd ~/kernel/kernel-4.9
patch -p1 < ~/Downloads/a85f878b443f8d2b91ba76f09da21ac0af22e07f.patch
# Replace the nonexistent memset_l call with the regular memset
sed -i 's/memset_l(page, value, PAGE_SIZE \/ sizeof(unsigned long));/memset(page, value, PAGE_SIZE);/g' mm/zswap.c
Now that we’ve made all of the changes we wanted to the kernel, we need to build it and place it on the /boot partition.
This will take a while.
# Make sure we're in the kernel directory
cd ~/kernel/kernel-4.9
make -j5 # -j denotes the number of threads
sudo make modules_install
sudo cp /boot/Image /boot/Image.dist
sudo cp arch/arm64/boot/Image /boot
After this is done, you can reboot. If the Nano boots successfully, it means that you’re running your new custom kernel. You can run uname -r to check it:
# The stock kernel returns '4.9.140-tegra'
# Your custom kernel should return only '4.9.140'
uname -r
If all is well, we can transfer our root partition to the external SSD.
This will also take a while when you get to the copying of the root filesystem to the SSD.
See steps below:
# Wipe the partition table
sudo dd if=/dev/zero of=/dev/sda bs=1M count=1
# Create a GPT partition table, then create a new EXT4 volume
# Create a Linux swap partition (4GB) - arrow over to "Type" and select "Linux swap"
# Go over to "Write" and type "yes" and then quit
sudo cfdisk /dev/sda
# Make an ext4 volume and a swap partition
sudo mkfs.ext4 /dev/sda1
sudo mkswap /dev/sda2
# Mount the partition and copy the root filesystem to it
sudo mkdir /mnt/root
sudo mount /dev/sda1 /mnt/root
sudo mkdir /mnt/root/proc
sudo apt -y install rsync
sudo rsync -axHAWX --numeric-ids --info=progress2 --exclude=/proc / /mnt/root
sudo sed -i 's/mmcblk0p1/sda1/' /boot/extlinux/extlinux.conf
sudo sed -i 's/rootwait/rootwait zswap.enabled=1/' /boot/extlinux/extlinux.conf
Reboot, and you should be running from the USB SSD.
If you mess something up and the Nano doesn’t boot, don’t worry, you can always plug the microSD into a Linux PC and mount it, go to the boot partition and open the extlinux/extlinux.conf file and replace sda1 with mmcblk0p1 so it boots from the microSD.
If you somehow end up messing up the extlinux.conf file, I’ve provided a copy for you down below, so you can overwrite it as stated in the tip above and boot back to the microSD:
TIMEOUT 30
DEFAULT primary
MENU TITLE p3450-porg eMMC boot options
LABEL primary
MENU LABEL primary kernel
LINUX /boot/Image
INITRD /boot/initrd
APPEND ${cbootargs} rootfstype=ext4 root=/dev/sda1 rw rootwait
One last thing to do after you boot from your SSD, enable swap:
echo "/dev/sda2 none swap \
defaults 0 1" | sudo tee -a /etc/fstab
And you’re done. Congrats on hacking the kernel and running your Nano from an external SSD. It’ll be worth it!