Devd doesn’t trigger LINK_UP

On FreeBSD, you can use devd to trigger scripts that react to device state changes. For instance, you plug/remove a device, or you connect/disconnect an Ethernet cable.

I had to use this kind of rule to restart a service when an interface is reconnected. However the rule would not trigger when the cable was reconnected.

The reason was that default rules in /etc/devd.conf were failing, hence stopping the execution of the next rules. In particular service dhclient quietstart $subsystem".

The solution was either to comment these lines in devd.conf or give my custom devd configuration a higher priority.

Override rc order in FreeBSD

In FreeBSD as in most other Operating Systems, the boot process consist of starting a set of scripts/services/daemons/processes. Each of those has constraints like depending-on or starting before other scripts for instance.

On a default FreeBSD install, this order would be determined by the packages you install, each of them installing a script in /usr/local/etc/rc.d that specifies its constraints requirements.

What, however, if you wanted to change the order of the boot process? For instance, you have a script that by default starts just after the network is ready, but in your case, it specifically has to start after another script for everything to work properly.

Well, I was confronted to that particular problem, and the answer is cross-dependency-scripts or whatever you want to call them.
Suppose that you have the following scripts in your boot process: A, B, C, D. By default, B, C and D start just after A. But you want to change that so B starts after D and C starts after B.

If you changed the order dependency in script B and C directly, that change would be overwritten on the next package update. Instead we add two empty scripts __B and __C that will just enforce the dependence. That is, __B starts after D and before B, __C starts after B and before C.

Looking at the code, at the beginning of the original scripts you would find:

-- rc.d/A

-- rc.d/B

-- rc.d/C

-- rc.d/D


Thus you would add two scripts __B and __D that contains:

-- rc.d/__B

-- rc.d/__C


FreeBSD on NanoPi R2S

The NanoPi R2S is a nice little ARM board that can serve nicely as a home router or firewall. It is comparable in power to a RaspberrryPi 3 with a 4 cores ARM Cortex-A53 SoC, namely the Rockchip RK3328. It has no video or sound output, only a handful GPIOs compared to a RPi, but it has one great advantage. It has 2 Ethernet ports, one of them is a true Gigabit internal card and the second is also a Gigabit connected through the internal USB3, so it’s a bit slower. On the RPi3, if you want 2 Gigabit Ethernet ports, you need an external card and both ports would run on the USB3, drastically limiting the bandwidth.

They generally come with a heatsink and a fan integrated as the board is quite small compared to a RPi and heats up more easily. People generally use this board with OpenWrt or Ubuntu. In the past, I also used it with Armbian as my home network gateway. People also managed to run it on OpenBSD. But this post focuses on FreeBSD.

ARM64 has been a tier-1 architecture on FreeBSD for more than a year now, which I discussed in this post. I managed to run the RPi 2B+, 3 and 4 easily, but my attempts with the Nanopi R2S proved rather unsuccessful and had their fair share of kernel panics.

Fast forward one year, and we are now at FreeBSD 13.1-p1. So I recently gave it another try, and not only is the whole experience much more stable, the installing process is also much easier and straightforward.


To install FreeBSD, I used the image provided at PersonalBSD. It’s a custom image based on a patched version of FreeBSD 13. It’s quite customized in fact, and point to its own repo by default. It starts sshd by default, and run both interfaces on DHCP. It has pubkey root login enabled and also has another admin user installed.

The rc.conf has growfs enabled, so the first boot will grow the ROOT UFS partition to the size of your SD card. It’s all GPT. There is a 256MB swap partition, but you can add a file based swap if you want. For instance in /etc/fstab:

md99 none swap sw,file=/.swap,late 0 0

The root partition has the following options enabled:

tunefs: POSIX.1e ACLs: (-a)                                disabled
tunefs: NFSv4 ACLs: (-N)                                   disabled
tunefs: MAC multilabel: (-l)                               disabled
tunefs: soft updates: (-n)                                 disabled
tunefs: soft update journaling: (-j)                       disabled
tunefs: gjournal: (-J)                                     disabled
tunefs: trim: (-t)                                         disabled
tunefs: maximum blocks per file in a cylinder group: (-e)  4096
tunefs: average file size: (-f)                            16384
tunefs: average number of files in a directory: (-s)       64
tunefs: minimum percentage of free space: (-m)             8%
tunefs: space to hold for metadata blocks: (-k)            2768
tunefs: optimization preference: (-o)                      space

It has soft updates disabled, which means that if the board has a hard reboot, you are in for some fsck nightmare. You can enable softupdates offline when the card is connected to your computer (running FreeBSD) for instance:

tunefs -n /dev/da0p2

I highly recommend to enable soft updates to save you much hassle.

Playing around

By default there is no EEPROM for the MAC addresses so they are randomly generated on each boot. Keep this in mind if you want to use IPv6 and SLAAC. There is also three status LEDs on the board. You can control them from the command line with echo 0|1 > /dev/led/nanopi-r2s:*. The custom image comes with a daemon and rc.local shell lines to setup a default state for the LEDs, but you can easily disable it.

The two interfaces are dwc0 for the Rockchip internal card, and ue0 for the card connected through USB3. Here is a quick test with iperf3 using UDP on both interfaces. Results are given in Mbits/sec.

dwc0 765 946
ue0 784 370


Running it

But does it fit an use for a full fledged Internet gateway? I ran the Nanopi R2S for some time now as a home network gateway, and while it worked very well most of the time, the R2S would from time to time become completely inaccessible. Although it did not seem to panic as scripts where still running behind the scene. I could not identify what the problem was, but I know that the problem happened when either the Ethernet cable on the LAN interface was disconnected, or when too much traffic arrived on the Ethernet port.

AMD Renoir GPU on FreeBSD 13 (bis)

In a previous post, I explained that I backported the 5.5-stable branch of drm-kmod and got it running on FreeBSD 13.0-RELEASE although some bugs persisted.

Since then I’ve received several requests if it was possible to access the code or if a port was available. However the answer was always that it was a quick and dirty patch, panic do occur and that it wasn’t published on purpose (but a tarball was available).

But I’ve been at it again. I first tried to backport the 5.7-stable branch to FreeBSD 13. It did compile and display worked. But there were several bugs that I was unable to fix.

So I restarted the patch over 5.5-stable but this time dedicated more than 10 minutes for the task. The thing works and even fixed a bug in the process. Also it does now compile on a fresh FreeBSD 13.0-RELEASE ✌️

This new version is available on my github as 5.5-fbsd13release branch. Feel free to clone and try. Intel i915 is still not supported but could be if someone is interested and willing to try it.

Still no port tho, as I’m not confident at all in my understanding of drm-kmod and ability to contribute.

Find oldest file on UNIX/BSD

If you wish to find the oldest file in a directory tree on a UNIX system, you might have found the following solution:

find . -type f -printf '%T+ %p\n' | sort | head -n 1

This is all good and nice, but it only works with the GNU version of findutils. Indeed other versions of find do not support the -printf option. A more compatible option goes something like that (it’s at the same time more generic (doesn’t use -printf) and more BSD specific (stat syntax) but you might adapt it to Linux easily):

find . -type f | xargs stat -f "%m %N" | sort | head -n 1

AMD Renoir GPU on FreeBSD 13

UPDATE: github branch available (2022-03-12)

You if have a laptop running one of the latest AMD Ryzen 5 (such as the ThinkPad T14 AMD) with integrated Radeon graphic on FreeBSD 13, you probably struggled to get the GPU running and finally gave up to use the generic scfb frame buffer device instead. It is non accelerated, you cannot access any external port nor adjust the brightness, but it’s usable.

Now I’ve some good news for you! After randomly searching the FreeBSD forums I found a post that was referring to a work-in-progress branch of drm-kmod that should support the latest AMD Renoir GPU. This branch is now named 5.5-stable. After some modifications I was able to compile it on FreeBSD 13.0-RELEASE-p3 and got it to run on the GENERIC kernel.

Acceleration and the external ports work well. I even managed to get a 3 screen setup working through one of those USB-C docking station.

Only one cable connected to the laptop that carries the power supply, audio, external mouse, two screens, external HDD, floppy drive and more. Now, this is what I call “Universal Serial Bus”.

Patching the drm-kmod 5.5-stable branch to compile on 13.0-RELEASE was pretty straightforward too. A missing function here, a different signature there, some missing constants. A lot easier than other things I’ve been trying to port from 12 to 13.

Since I’ve also been able to install a modern TeX Live environment (the 2015 version from the port tree is getting rather outdated), I can now again use FreeBSD as my main working Operating System. Yahoo!

FreeBSD on RaspberryPi

RaspberryYou may have heard that ARM64 is now a tier-1 platform on FreeBSD 13. This basically means that this platform is now fully supported by the various FreeBSD teams. Seeing ARM supported as a tier 1 is really cool, since we can now play around with those ARM boards (RaspberryPi, BeagleBone, …) that were so far reserved to a selected set of Linux distributions. Being able to use an entirely different OS is refreshing.

In the last few weeks, I’ve devoted some of the very little free time that I have to try just that. I’ve installed FreeBSD on some RPi3 boards in the hope of replacing whatever they were doing before on Raspbian. If you insist you can use the video, but it’s very slow and totally unusable as a Kodi media-center. The reason being (as far as I understand) that the VideoCore drivers are a pain in the ass to port even from RPi Linux flavor to another, let alone on FreeBSD. So I wouldn’t expect improvement on that front for some time.

However as a network appliance or audio-only media-center, it works nicely. It’s just a matter of downloading the appropriate SD card image for your specific board (here and there), dd it to the SD card, boot with HDMI and USB keyboard, then configure your installation from there. Beware though, you should avoid editing the UFS partition of the image until it has been resized by growfs_enable=YES. In my case that triggered some naughty kernel panics (which I had no time to investigate thoroughly, oops).

I’ve also tried some unsupported platform such as the NanoPi R2S with mitigated success. I managed to boot on the serial interface and install some packages. But USB devices are not (or not always) showing, the driver for the second Ethernet interface is missing, and random kernel panics do happen. So I gave up for now and tried an RPi3 board instead.

I also tried running it on a RaspberryPi 1 B+ v1.2. The reason I’m trying that is to replace my media-center setup, especially the audio part. The RPi1 should consume less power, is perfectly capable for the task at hand and I’ve got a lot of them lying around. Since the device will be always on, it should be a perfect replacement for the RPi3 I used so far. For this I had to use the RPi-B image. Contrary to the RPi3 which runs ARMv8 64-bit, RPi1 B+ is still an ARMv6 32-bit architecture and a tier-2 platform on FreeBSD.

One of the problem with the RaspberryPi is that there are a lot of different version nowadays and they all share similar names. For instance if you say RPi B+, is it RPi3 B+ from 2018, RPi1 B+ from 2014, a confusion with the RPi2 B v1.2 from 2016 or the RPi2 B? All these run on different SoC with different flavor of ARM from ARMv6 to ARMv8. You must pay attention to choose the correct image for your board. Use the Wikipedia’s RaspberryPi page to tell them apart, it’s very complete, especially the Specifications and Connectors sections.

Clear zpool property

If you want to clear a zfs property, you will often be told to use inherit to instead just inherit the value from the parent dataset. But what if you want to clear a zpool property instead? The answer is simple but you got to know it.

Here I put the '' to emphasize the fact that it’s a pure empty space. that is, if you want to clear the property, there is nothing after ‘=’.

zpool set <property>='' <pool>

Replace USB boot key on FreeNAS

You don’t really install FreeNAS as you do with some other OS. Instead you have a device (often an USB key) that boots FreeNAS which itself manages your data storage. Indeed why wasting some precious SATA port for a boot device when all you need is a little USB key? Especially when thits boot device is less than 2GB in size.

The problem with USB keys is that they wear out over time. So it inevitably comes a time when it must be replaced. This is what happened to me a few days ago.

The freenas-boot pool switched to a DEGRADED state because checksum error happened too frequently. Fortunately a zpool scrub freenas-boot detected no error, and it seems that zfs was still able to correct those. So instead of reflashing a new USB key and restoring my config backup, I could from FreeNAS itself create a new bootable USB key and replace replace the faulty device in the freenas-boot pool.

Let’s login on FreeNAS and plug in your new USB key. We first find and verify what is the device name for the USB key, in my case it’s da2:

$ gpart part show da2
=>       1  60088319  da2  MBR  (29G)
         1        31       - free -  (16K)
        32  60088288    1  fat32lba  (29G)

You see that MBR table and a FAT32 partition, in my case it can only be the new USB key. We will replace that with GPT partitions. This will contain a boot partition (MBR boot or EFI boot), and a ZFS partition. As root:

# Clean that MBR and create GPT partition table
gpart destroy -F da2
gpart create -s GPT da2

# If you use MBR boot
gpart add -s 64K -t freebsd-boot da2
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da2

# If you use EFI boot
gpart add -s 128M -t efi da2
gpart bootcode -p /boot/boot1.efifat -i 1 da2

# Create the ZFS partition
gpart add -t freebsd-zfs da2

Now it’s time to replace the device:

$ zpool replace -f freenas-boot da1p2 da2p2
cannot replace da1p2 with da2p2: device is too small
# OOPS! :(

If you got the message device is too small, that is because you’ve been trying to replace a device with another one that is smaller than the original (in term of ZFS partition size I suppose). ZFS requires that you replace a device with another with a size greater or equal to the old one. This can be a problem especially if you have a ZFS partition along with others in the partition table like here, or if the vendor announces a certain disk size but only within a certain approximation. You could result in a disk announced to be the exact same size ending up to be very slighty shorter than your original disk. And as a result you won’t be able to use it to replace any disk in your ZFS pool/vdev. That’s why some recommend to create ZFS filesystems several percents less than the available disk size.

But since we messed up, instead we will create a pool on the other USB key and use ZFS send/recv to migrate the files.

# Create the pool on the new USB key.
# Note that we have to call it freenas-boot2
# because freenas-boot already exists.
zpool create freenas-boot2 da2p2 

# Create a recursive snapshot of the original pool
# that we will use to restore the backup. 
zfs snapshot -r freenas-boot@restore

# Send the snapshot from the old to the new USB key.
zfs send -R freenas-boot@restore | zfs recv -F freenas-boot2

We also need to save the original setting for bootfs because FreeNAS has its own way of managing the boot pool. We will set this value later on the new pool.

$ zpool get bootfs
NAME          PROPERTY  VALUE                        SOURCE
freenas-boot  bootfs    freenas-boot/ROOT/11.3-U4.1  local

We still have to change that name from freenas-boot2 to freenas-boot. However we cannot do so on the running instance of FreeNAS because it already has a pool named freenas-boot. So, we will import it on another ZFS capable machine and fix its name.

(but if anyone knows how to do this live, I’m interested)

# On some other ZFS capable host (FreeBSD for instance ;) )
# import the pool and change its name back to freenas-boot.
# The -f is required because it comes from another host.
zpool import -f freenas-boot2 freenas-boot

# Configure default boot filesystem.
# Remember the value of bootfs we found before on the original pool.
zpool set bootfs=freenas-boot/ROOT/11.3-U4.1 freenas-boot

# Export the pool again.
zpool export freenas-boot

Finally you can clear the old snapshot:

zfs destroy -R freebsd-boot@restore