Linux, Unix, and whatever they call that world these days

Supermicro IPMI Password Reset

For a long while I had issues with my AMD Epyc Supermicro board. While the issues “smelled” like memory, it wasn’t easy to pinpoint why exactly the system would get stuck couple times a week. It all pointed either toward the motherboard or, more likely, the processor itself. Either way, I decided to obtain an equivalent board.

Being cheap bastard, I didn’t want to buy it new but decided to peruse local Craigslist (no luck) and eBay. Being my lucky day, I got a really good price on a similar but not exactly the same board. Yes, the new board was Intel, used slightly more watts, less memory bandwidth, and fewer LAN ports. That said, it allowed me to use the same ECC memory I used on the AMD board, and those 2 LAN ports were of the 10G variant which does make me want to buy a new switch. Suffice to say, I don’t consider it a downgrade.

But, as always when used boards are bought, one doesn’t necessarily have all the passwords. In my case, the missing password was for the remote management interface (IPMI). Clearing CMOS was of no help.

I did search the Internet for solutions but none worked exactly as written. After slight modifications, I managed to erase my IPMI configuration so I might as well share what worked for me in 2024. I am sure that in a couple of years, these instructions will be incomplete too but that’s a problem for a future me.

First of all, you need IPMI tools. I downloaded them directly from Supermicro as these low-level operations tend not to work properly when using generic tools you might have with your Linux distribution. And yes, these instructions will be Linux-based. If you have something else, check the tools regardless as they contain executables for other OS’ too.

For Linux, we want to extract the IPMICFG-Linux.x86_64 file and then allow for its execution by setting the x bit:

chmod +x ./IPMICFG-Linux.x86_64

An excellent way to check if the tool is working (and to double-check if you’re on the correct host) is to see what the current IPMI settings actually are. Run:

./IPMICFG-Linux.x86_64 -m

Finally, we can fully reset IPMI by completely erasing its configuration (-fde) followed by a factory reset of the actual use Some guides would have you just perform a user reset but that didn’t work for me. I had to reset both and in this order:

./IPMICFG-Linux.x86_64 -fde -d
./IPMICFG-Linux.x86_64 -fd 3 -d

Finally, the usual ADMIN/ADMIN can be used to get into IPMI.

ZFS Encryption Speed (Ubuntu 24.04)

Well, another Ubuntu version, another set of encryption performance tests. Here are the results for Ubuntu 24.04 on kernel 6.8 using ZFS 2.2.2. As I’m doing this for quite a few versions now, you can find older tests for Ubuntu 23.10, 23.04, 22.10, 22.04, 20.10, and 20.04.

Testing was done on a Framework laptop with an i5-1135G7 processor and 64GB of RAM. Once booted into installation media, I execute the script that creates a 42 GiB RAM disk that hosts all data for six 6 GiB files. Those files are then used in a RAIDZ2 configuration to create a ZFS pool. The process is repeated multiple times to test all different native ZFS encryption modes in addition to a LUKS-based test. This whole process is repeated again with AES disabled. As before, the test is a simple DD copy of 4 GB files; however, this time I included FIO tests for sequential and random read/write. One thing absent for the 24.04 round is a 2-core run. Relative performance between a 2-core and 4-core setup remained about the same over many years I’ve been doing this testing and thus it doesn’t really seem worth the effort.

Illustration

Since I am testing on the same hardware as previously, I expected little to no difference in performance but I was pleasantly surprised as performance did significantly increase across the board by about 20%. Considering 23.10 decreased performance by 10%, it’s nice to see we have that performance recovered with a bit of improvement on top. If you need more disk performance out of your existing hardware, you should really consider upgrading to Ubuntu 24.04.

When it comes to the relative performance, nothing really changed. ZFS encryption is still more performant than LUKS on writes and LUKS exhibits slightly higher performance when it comes to reads. CCM modes are still atrocious but, if your processor doesn’t have AES support, might be useful.

Illustration

As, going forward, I plan to use FIO instead of a simple dd copy, it’s as good time to analyze those numbers too. Unsurprisingly, the sequential performance numbers as compared to the simple DD copy are about the same. The only outlier seems to be read performance that drops a bit more than other readings. My best guess is that this is due to higher parallel IO demands FIO makes.

Illustration

Since I am using FIO, I decided to add random I/O too. I expected results to be lower but numbers surprised me still. Write performance dropped to 50 MB/s without encryption. With encryption performance drops even further to 30 MB/s. Fortunately, real loads are not as unforgiving as FIO so you can expect much better performance in real-life.

In future, there are a few things I plan to change. First of all, I plan to switch onto using FIO instead of DD. While I will probably still collect DD data, it will just be there so one can compare it more easily to older tests and not as a main tool. Secondly, I plan to switch LUKS to 4K blocks and not bother measuring 512-byte sector size at all. Most of drives these days have 4K sectors and thus it makes sense that any proper LUKS installation would match that sector size. Making it default just makes sense. Performance-wise, they’re not a huge improvement but the do bring LUKS numbers closer to the native encryption.


PS: Raw data is available in Google Sheets.

AMD processor temperature under Ubuntu 24.04

I often like to check my laptop’s temperature when I am doing something that requires a lot of power. I found knowing temperature really helps with understanding where the limits lie. However, my old scripts that worked on Intel systems doesn’t work on AMD. So I went to research it a bit.

After a bit of snooping around, all the data can be found under /sys/class/hwmon/. It’s there where we can find multiple _label files which describe a temperature source. The one we’re after is Tctl. Once we look over all of these, THERMAL_SOURCES variable should contain the file path (or more of them) for the temperature expressed in thousands of ℃.

for THERMAL_LABEL_FILE in `find /sys/class/hwmon/hwmon?/ -type f -name "temp*_label" -print`; do
    THERMAL_LABEL=`cat "$THERMAL_LABEL_FILE"`
    if [ "$THERMAL_LABEL" = "Tctl" ]; then
        THERMAL_SOURCES="`echo $THERMAL_LABEL_FILE | sed 's/_label$/_input/g'`"
    fi
done

Knowing which file contains a temperature is only the first part. What I like to do next is to fold all temperatures (if multiple sources exist) into a single figure by selecting the maximum value. Then, it’s just a matter of moving the decimal point around to get a while number reading.

TEMP_ALL="$(cat $THERMAL_SOURCES | awk '{print $1}' | sort -n)"
TEMP_MAX="$(echo "$TEMP_ALL" | tail -n 1 | awk '{print int(($1 + 500) / 1000) }')"

Manual Grub Boot for ZFS Root

As I was messing with making my EFI partition larger, I managed to corrupt the system. My best guess was that my new partition sizes weren’t properly (re)loaded before I formated them. Thus, even though both boot and EFI partitions had all files properly restored, during boot I would end up dropped into the Grub prompt.

While I do not often end up in such situation, I already know grub from my Surface Go adventures. So I did what I had done many times before (gpt2 is my boot partition):

set root=(hd0,gpt2)
linux /vmlinuz-6.8.0-28-generic
initrd /initrd.img-6.8.0-28-generic

This moved needle a bit by dumped me into the initramfs prompt. At least here it did helpfully indicate that the issue was (corrupted disk). However, it was obvious something was still wrong as my root ZFS partition was nowhere to be found. Thus, no fsck to fix the issue.

Initial thought was to just load ZFS filesystem:

zpool import Tank/System
zfs mount Tank/System
exit

Well, this actually caused the system to crash as filesystem wasn’t properly overlaid. So I had to figure out either how to reload the root partition from the initramfs prompt or to go back to the drawing board.

Thankfully, Looking at my other computer’s Grub configuration, I noticed the way forward. There, I saw that linux command has an extra ZFS-related argument. Thus, I adjusted my grub commands accordingly (the example below assumes the root dataset is Tank/System):

set root=(hd0,gpt2)
linux /vmlinuz-6.8.0-28-generic root=ZFS=Tank/System
initrd /initrd.img-6.8.0-28-generic
boot

And this brought my system back to its bootable self.


PS: Since the boot file system was actually readable, I decided to simply copy files to a temporary location, format both boot and EFI partitions, and then copy the data back.

mkdir /mnt/{efi,boot}-copy
rsync -avxAHWX /boot/efi/ /mnt/efi-copy/
rsync -avxAHWX /boot/     /mnt/boot-copy/

umount /boot/efi
umount /boot

DISK1=</dev/disk/by-id/...>
yes | mkfs.ext4 $DISK1-part2
mkfs.vfat -F 32 -n EFI -i 4d65646f $DISK1-part1

mount /boot
mount /boot/efi
rsync -avxAHWX /mnt/boot-copy/ /boot/
rsync -avxAHWX /mnt/efi-copy/  /boot/efi/

rm -rf /mnt/{boot,efi}-copy

[2024-10-05] If you didn’t copy all permissions for files, you might need to reapply grub too:

grub-install --target=x86_64-efi --efi-directory=/boot/efi \
    --bootloader-id=Ubuntu --recheck --no-floppy

Trimming USB Disk

With Linux, the easiest way to not only delete the whole drive but to also trim it at the same time is blkdiscard command. Combine that with an external M.2 USB storage and you can clean up pretty much any drive. But what if your drive tells you “ioctl failed: Operation not supported”? Well, then it’s time for some udev trickery.

For me this issue happened with my Sabrent USB Enclosure which I found a really useful for accessing M.2 SSDs. Not only it supports both NVMe and SATA drives but it also has rarely decent tooless mechanism. And all that at a reasonable cost.

The only downside was not supporting trim operation directly. However, this was not due to the device itself but just due to Ubuntu not recognizing its capabilities. And all we need to correct this is its vendor and product ID which can be easily found using lsusb command.

With those two parameters, we can create a special rule telling Linux to allow trim (unmap) operation.

cat << EOF | sudo tee /etc/udev/rules.d/42-sabrent-storage.rules
ACTION=="add|change", SUBSYSTEM=="scsi_disk", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="9210", ATTR{provisioning_mode}:="unmap"
EOF

To apply this without reboot, just reload all rules:

sudo udevadm control --reload-rules && sudo udevadm trigger

And that’s it. Now you can use blkdiscard, fstrim, or whatever other trimming method you love.

Mirrored ZFS on Ubuntu 23.10

One reason why I was excited about Framework 16 was to get two NVMe slots. While I was slightly disappointed by the fact the second slot could only handle 2230 M.2 SSD (instead of the full size 2280), having two slots makes dual boot easier to deal with. Alternatively, for ZFS aficionados like myself, it allows for data mirroring.

With both M.2 slots filled, I decided to set up UEFI boot ZFS mirror with LUKS-based encryption. Yes, I know that native encryption exists on ZFS and it might even have some advantages when it comes to performance.

Another thing you’ll notice about my installation procedure is the number of manual steps. While you can use the normal installer and then add mirroring later, I generally like manual installation better as it gives me freedom to set up partitions as I like them.

Lastly, I am going with Ubuntu 23.10 which is not officially supported by Framework. I found it works for me, but your mileage may vary.

With that out of the way, we can start installation by booting from USB, going to terminal, and becoming root:

sudo -i

Now we can set up a few variables - disk, pool, host name, and user name. This way we can use them going forward and avoid accidental mistakes. Just make sure to replace these values with ones appropriate for your system.

DISK1=/dev/disk/by-id/<firstdiskid>
DISK2=/dev/disk/by-id/<seconddiskid>
POOL=mypool
HOST=myhost
USER=myuser

The general idea of my disk setup is to maximize the amount of space available for the pool with the minimum of supporting partitions. However, you will find these partitions are a bit larger than what you can see at other places - especially when it comes to the boot and swap partitions. You can reduce either but I found having them oversized is beneficial for future proofing. Also, I intentionally make both the EFI and boot partition share the same UUID. This will come in handy later. And yes, you need swap partition no matter how much RAM you have (unless you really hate the hibernation).

In either case, we can create them all:

DISK1_ENDSECTOR=$(( `blockdev --getsz $DISK1` / 2048 * 2048 - 2048 - 1 ))
DISK2_ENDSECTOR=$(( `blockdev --getsz $DISK2` / 2048 * 2048 - 2048 - 1 ))

blkdiscard -f $DISK1 2>/dev/null
sgdisk --zap-all                                $DISK1
sgdisk -n1:1M:+63M            -t1:EF00 -c1:EFI  $DISK1
sgdisk -n2:0:+1984M           -t2:8300 -c2:Boot $DISK1
sgdisk -n3:0:+64G             -t3:8200 -c3:Swap $DISK1
sgdisk -n4:0:$DISK1_ENDSECTOR -t4:8309 -c4:LUKS $DISK1
sgdisk --print                                  $DISK1

PART1UUID=`blkid -s PARTUUID -o value $DISK1-part1`
PART2UUID=`blkid -s PARTUUID -o value $DISK1-part2`

blkdiscard -f $DISK2 2>/dev/null
sgdisk --zap-all                                               $DISK2
sgdisk -n1:1M:+63M            -t1:EF00 -c1:EFI  -u1:$PART1UUID $DISK2
sgdisk -n2:0:+1984M           -t2:8300 -c2:Boot -u2:$PART2UUID $DISK2
sgdisk -n3:0:+64G             -t3:8200 -c3:Swap -u3:R          $DISK2
sgdisk -n4:0:$DISK2_ENDSECTOR -t4:8309 -c4:LUKS -u4:R          $DISK2
sgdisk --print                                                 $DISK2

Since I use LUKS, I get to encrypt my ZFS partition now.

cryptsetup luksFormat -q --type luks2 \
    --sector-size 4096 \
    --perf-no_write_workqueue --perf-no_read_workqueue \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i $DISK1-part4

cryptsetup luksFormat -q --type luks2 \
    --sector-size 4096 \
    --perf-no_write_workqueue --perf-no_read_workqueue \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i $DISK2-part4

Of course, encrypting swap is needed too. Here I use the same password as one I used for data. Why? Because that way you get to unlock them both with a single password prompt. Of course, if you wish, you can have different password too.

cryptsetup luksFormat -q --type luks2 \
    --sector-size 4096 \
    --perf-no_write_workqueue --perf-no_read_workqueue \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i $DISK1-part3

cryptsetup luksFormat -q --type luks2 \
    --sector-size 4096 \
    --perf-no_write_workqueue --perf-no_read_workqueue \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i $DISK2-part3

Now we decrypt all those partitions so we can fill them with sweet, sweet data.

cryptsetup luksOpen $DISK1-part4 ${DISK1##*/}-part4
cryptsetup luksOpen $DISK2-part4 ${DISK2##*/}-part4

cryptsetup luksOpen $DISK1-part3 ${DISK1##*/}-part3
cryptsetup luksOpen $DISK2-part3 ${DISK2##*/}-part3

Finally, we can create our mirrored pool and any datasets you might want. I usually have a few more but root (/) and home (/home) partition are minimum:

zpool create -o ashift=12 -o autotrim=on \
    -O compression=lz4 -O normalization=formD \
    -O acltype=posixacl -O xattr=sa -O dnodesize=auto -O atime=off \
    -O canmount=off -O mountpoint=none -R /mnt/install \
    $POOL mirror /dev/mapper/${DISK1##*/}-part4 /dev/mapper/${DISK2##*/}-part4

zfs create -o canmount=noauto -o mountpoint=/ \
           -o reservation=64G \
           ${HOST^}/System
zfs mount ${HOST^}/System

zfs create -o canmount=noauto -o mountpoint=/home \
           -o quota=128G \
           ${HOST^}/Home
zfs mount ${HOST^}/Home
zfs set canmount=on ${HOST^}/Home

zfs set devices=off ${HOST^}

Now we can format swap partition:

mkswap /dev/mapper/${DISK1##*/}-part3
mkswap /dev/mapper/${DISK2##*/}-part3

Assuming UEFI boot, I like to have ext4 partition here instead of more common ZFS pool as having encryption makes it overly complicated otherwise.

yes | mkfs.ext4 $DISK1-part2
mkdir /mnt/install/boot
mount $DISK1-part2 /mnt/install/boot/

Lastly, we need to format EFI partition:

mkfs.msdos -F 32 -n EFI -i 4d65646f $DISK1-part1
mkdir /mnt/install/boot/efi
mount $DISK1-part1 /mnt/install/boot/efi

And, only now we’re ready to copy system files. This will take a while.

apt update
apt install --yes debootstrap
debootstrap mantic /mnt/install/

Before using our newly copied system to finish installation, we can set a few files.

echo $HOST > /mnt/install/etc/hostname
sed "s/ubuntu/$HOST/" /etc/hosts > /mnt/install/etc/hosts
sed '/cdrom/d' /etc/apt/sources.list > /mnt/install/etc/apt/sources.list
cp /etc/netplan/*.yaml /mnt/install/etc/netplan/

Finally, we can login into our new semi-installed system using chroot:

mount --rbind /dev  /mnt/install/dev
mount --rbind /proc /mnt/install/proc
mount --rbind /sys  /mnt/install/sys
chroot /mnt/install /usr/bin/env \
    DISK1=$DISK1 DISK2=$DISK2 HOST=$HOST USER=$USER \
    bash --login

My next step is usually setting up locale and time-zone. Since I sometimes dual-boot, I found using local time in BIOS works the best.

locale-gen --purge "en_US.UTF-8"
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales

ln -sf /usr/share/zoneinfo/PST8PDT  /etc/localtime
dpkg-reconfigure -f noninteractive tzdata

echo UTC=no >> /etc/default/rc5

Now we’re ready to onboard the latest Linux image.

apt update
apt install --yes --no-install-recommends linux-image-generic linux-headers-generic

To allow for decrypting, we need to update crypttab:

echo "${DISK1##*/}-part4 $DISK1-part4 none \
      luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
echo "${DISK1##*/}-part3 $DISK1-part3 none \
      luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
echo "${DISK2##*/}-part4 $DISK2-part4 none \
      luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
echo "${DISK2##*/}-part3 $DISK2-part3 none \
      luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
cat /etc/crypttab

And, of course, all those drives need to be mounted too. Please note that the last two entries are not really needed, but I like to have them as it prevents Ubuntu from cluttering the taskbar otherwise.

echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK1-part2) \
    /boot ext4 nofail,noatime,x-systemd.device-timeout=3s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK1-part1) \
    /boot/efi vfat nofail,noatime,x-systemd.device-timeout=3s 0 1" >> /etc/fstab
echo "/dev/mapper/${DISK1##*/}-part3 \
    swap swap nofail 0 0" >> /etc/fstab
echo "/dev/mapper/${DISK2##*/}-part3 \
    swap swap nofail 0 0" >> /etc/fstab
echo "/dev/mapper/${DISK1##*/}-part4 \
    none auto nofail,nosuid,nodev,noauto 0 0" >> /etc/fstab
echo "/dev/mapper/${DISK2##*/}-part4 \
    none auto nofail,nosuid,nodev,noauto 0 0" >> /etc/fstab
cat /etc/fstab

Next we can proceed with setting up the boot environment:

apt install --yes zfs-initramfs cryptsetup keyutils grub-efi-amd64-signed shim-signed

KERNEL=`ls /usr/lib/modules/ | cut -d/ -f1 | sed 's/linux-image-//'`
update-initramfs -c -k all

To be able to actually use that boot environment, we install Grub too:

apt install --yes grub-efi-amd64-signed shim-signed
sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT.*/GRUB_CMDLINE_LINUX_DEFAULT=\"quiet splash \
    RESUME=UUID=$(blkid -s UUID -o value /dev/mapper/${DISK1##*/}-part3)\"/" \
    /etc/default/grub
update-grub
grub-install --target=x86_64-efi --efi-directory=/boot/efi \
    --bootloader-id=Ubuntu --recheck --no-floppy

With most of the system setup done, we get to install (minimum) Desktop packages:

apt install --yes ubuntu-desktop-minimal

To ensure the system wakes up with firewall, you can get iptables running:

apt install --yes man iptables iptables-persistent

iptables -F
iptables -X
iptables -Z
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

ip6tables -F
ip6tables -X
ip6tables -Z
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT

netfilter-persistent save
echo ; iptables -L ; echo; ip6tables -L

Some people like the snap packaging system and those people are wrong. If you are one of those that share this belief, you can remove snap too:

apt remove --yes snapd
echo 'Package: snapd'    > /etc/apt/preferences.d/snapd
echo 'Pin: release *'   >> /etc/apt/preferences.d/snapd
echo 'Pin-Priority: -1' >> /etc/apt/preferences.d/snapd

Since our snap removal also got rid of Firefox, we can add it manually:

add-apt-repository --yes ppa:mozillateam/ppa
cat << 'EOF' | sed 's/^    //' | tee /etc/apt/preferences.d/mozillateamppa
    Package: firefox*
    Pin: release o=LP-PPA-mozillateam
    Pin-Priority: 501
EOF
apt update && apt install --yes firefox

To have a bit wider software selection, adding universe repo comes in handy:

add-apt-repository --yes universe
apt update

And, since Framework 16 is AMD-based, adding AMD PPA is a must:

add-apt-repository --yes ppa:superm1/ppd
apt update

Also, since Framework 16 is new, we need to update the keyboard definition too (this step might not be necessary in the future):

cat << EOF | sudo tee -a /usr/share/libinput/50-framework.quirks
[Framework Laptop 16 Keyboard Module]
MatchName=Framework Laptop 16 Keyboard Module*
MatchUdevType=keyboard
MatchDMIModalias=dmi:*svnFramework:pnLaptop16*
AttrKeyboardIntegration=internal
EOF

Fully optional is also setup for hibernation. It starts with setting up the sleep configuration:

sed -i 's/.*AllowSuspend=.*/AllowSuspend=yes/' \\
    /etc/systemd/sleep.conf
sed -i 's/.*AllowHibernation=.*/AllowHibernation=yes/' \\
    /etc/systemd/sleep.conf
sed -i 's/.*AllowSuspendThenHibernate=.*/AllowSuspendThenHibernate=yes/' \\
    /etc/systemd/sleep.conf
sed -i 's/.*HibernateDelaySec=.*/HibernateDelaySec=13min/' \\
    /etc/systemd/sleep.conf

And continues with button setup:

apt install -y pm-utils

sed -i 's/.*HandlePowerKey=.*/HandlePowerKey=hibernate/' \\
    /etc/systemd/logind.conf
sed -i 's/.*HandleLidSwitch=.*/HandleLidSwitch=suspend-then-hibernate/' \\
    /etc/systemd/logind.conf
sed -i 's/.*HandleLidSwitchExternalPower=.*/HandleLidSwitchExternalPower=suspend-then-hibernate/' \\
    /etc/systemd/logind.conf

With all system stuff done, we finally get to create our new user:

adduser --disabled-password --gecos '' -u $USERID $USER
usermod -a -G adm,cdrom,dialout,dip,lpadmin,plugdev,sudo,tty $USER
echo "$USER ALL=NOPASSWD:ALL" > /etc/sudoers.d/$USER
passwd $USER

Now we can exit back into the installer:

exit

Don’t forget to properly clean our mount points in order to have the system boot:

umount /mnt/install/boot/efi
umount /mnt/install/boot
mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {}
zpool export -a

Finally, we are just a reboot away from success:

reboot

Once we login, there are just a few finishing touches. For example, I like to increase text size:

gsettings set org.gnome.desktop.interface text-scaling-factor 1.25

If you still remember where we started, you’ll notice that, while data is mirrored, our EFI and boot partition are not. My preferred way of keeping them in sync is by using my own utility named syncbootpart:

wget -O- http://packages.medo64.com/keys/medo64.asc | sudo tee /etc/apt/trusted.gpg.d/medo64.asc
echo "deb http://packages.medo64.com/deb stable main" | sudo tee /etc/apt/sources.list.d/medo64.list
sudo apt-get update
sudo apt-get install -y syncbootpart
sudo syncbootpart

sudo update-initramfs -u -k all
sudo update-grub

This utility will find what your currently used boot and EFI partition are and copy it to the second disk (using UUID in order to match them). And, every time a new kernel is installed, it will copy it to the second disk too. Since both disks share UUID, BIOS will boot from whatever it finds first and you can lose either drive while preserving your “bootability”.

At last, with all manual steps completed, we can enjoy our new system.

Framework Keyboard Is Not Quirky Enough

As a clumsy writer, one thing I noticed immediately on my Framework 16 was that my mouse cursor was running wild due to the touchpad not being disabled while I was typing. Within Ubuntu one would usually control that using disable-while-typing setting, i.e., something like this:

gsettings set org.gnome.desktop.peripherals.touchpad disable-while-typing 'true'

However, this was already set appropriately on Ubuntu 23.10. Set correctly, but not functioning. After troubleshooting a bit and reading a lot, I sorta had a working understanding. Libinput assumed that Framework 16 keyboard (due to it using USB connection) had nothing to do with the touchpad. Thus, it didn’t see any need to disable the said trackpad while someone is using the keyboard.

Fortunately, libinput has a solution for that - quirks. After testing a few things, I decided onto a following definition:

[Framework Laptop 16 Keyboard Module]
MatchName=Framework Laptop 16 Keyboard Module*
MatchUdevType=keyboard
MatchDMIModalias=dmi:*svnFramework:pnLaptop16*
AttrKeyboardIntegration=internal

Device name (MatchName) matches (hopefully) all Framework 16 keyboards (MatchUdevType) and we further limit device (MatchDMIModalias) to only Framework 16 laptops. Whatever survives all that matching will get pronounced an internal keyboard (AttrKeyboardIntegration).

To add that on your system, you can execute something like this

cat << EOF | sudo tee -a /usr/share/libinput/50-framework.quirks
[Framework Laptop 16 Keyboard Module]
MatchName=Framework Laptop 16 Keyboard Module*
MatchUdevType=keyboard
MatchDMIModalias=dmi:*svnFramework:pnLaptop16*
AttrKeyboardIntegration=internal
EOF

PS: If you want to do it for some other laptop, you can get most of the information from sudo libinput list-devices output and to match device, check /sys/class/dmi/id/modalias file.

PPS: There is a merge request for this. Hopefully, already the next libinput release will have it.

Preventing hibernation wake-up on Ubuntu

I have Ubuntu 23.10 with hibernation enabled on my Framework 13 but I noticed that it wakes up after a few minutes every time I put it into hibernation. That sort of defeats the purpose of hibernation so I had to investigate a bit.

My first step was checking what is enabled. Fortunately, we can find that information rather easily.

cat /proc/acpi/wakeup | grep enabled

After playing with a few things, I noticed that disabling XHCI actually does the trick most of the time.

echo "XHCI" | sudo tee /proc/acpi/wakeup

While this can be one solution, I wanted to be a bit more granular. So I started with listing all /sys devices that have a wakeup enabled.

for FILE in `sudo find /sys/devices -name 'wakeup' -print 2>/dev/null`; do
    if [[ -f $FILE ]] && [[ "`cat $FILE`" == "enabled" ]]; then
        dirname "`dirname "$FILE"`"
    fi
done

For each device found, you can check a few more details.

udevadm info -q all -a /sys/devices/pci0000:00/0000:00:15.3/i2c_designware.2/i2c-2/i2c-PIXA3854:00/

Based on those details, I would create an entry in /etc/udev/rules.d/42-disable-wakeup.rules for each suspicious device. For example, if I suspected my keyboard driver, I would create an entry like this.

ACTION=="add", SUBSYSTEM=="serio", DRIVER=="atkbd", ATTR{power/wakeup}="disabled"

Once I placed all suspicious entries in, I forced a rule reload using udevadm and tried hibernation out.

sudo udevadm control --reload-rules && sudo udevadm trigger
sudo systemctl hibernate

While this did solve my issue, it was overly restrictive. So, I removed entries one by one, testing hibernation each time. Once done, I had a list of devices that caused the wakeup isolated. On my Framework 13 with i5-1135G7, the winning combination file can be created using this command:

cat << EOF | sudo tee /etc/udev/rules.d/42-disable-wakeup.rules
ACTION=="add", SUBSYSTEM=="i2c", DRIVER=="i2c_hid_acpi", ATTRS{name}=="PIXA3854:00", ATTR{power/wakeup}="disabled"
ACTION=="add", SUBSYSTEM=="pci", DRIVER=="xhci_hcd", ATTRS{subsystem_device}=="0x0001", ATTRS{subsystem_vendor}=="0xf111", ATTR{power/wakeup}="disabled"
ACTION=="add", SUBSYSTEM=="serio", DRIVER=="atkbd", ATTR{power/wakeup}="disabled"
EOF

Your laptop might have a different list of culprits but the overall procedure should work the same.

[2025-01-11 Probably better way is creating a small pre-hibernate script that will hunt down these devices.]

Linux USB Auto-Suspend for FTDI

As I was playing with a few FTDI devices of mine, I noticed each was pulling 100 mA or more. Considering they were intended for use in a laptop, that made me worry a bit. Darn IC should have suspend and these numbers should not be the norm. So I went to investigate on how to reduce this a bit.

The first tool is, of course, lsusb. Using lsusb -tv I got the following output:

/:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
    ID 1d6b:0003 Linux Foundation 3.0 root hub
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M`
    ID 1d6b:0002 Linux Foundation 2.0 root hub
    |__ Port 3: Dev 60, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 12M
        ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
    |__ Port 7: Dev 3, If 0, Class=Video, Driver=uvcvideo, 480M
        ID 0bda:5634 Realtek Semiconductor Corp.
    |__ Port 7: Dev 3, If 1, Class=Video, Driver=uvcvideo, 480M
        ID 0bda:5634 Realtek Semiconductor Corp.
    |__ Port 9: Dev 4, If 0, Class=Vendor Specific Class, Driver=, 12M
        ID 27c6:609c Shenzhen Goodix Technology Co.,Ltd.
    |__ Port 10: Dev 5, If 0, Class=Wireless, Driver=btusb, 12M
        ID 8087:0026 Intel Corp. AX201 Bluetooth
    |__ Port 10: Dev 5, If 1, Class=Wireless, Driver=btusb, 12M
        ID 8087:0026 Intel Corp. AX201 Bluetooth
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
    ID 1d6b:0003 Linux Foundation 3.0 root hub
    |__ Port 3: Dev 2, If 0, Class=Mass Storage, Driver=uas, 5000M
        ID 0bc2:ab30 Seagate RSS LLC
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    ID 1d6b:0002 Linux Foundation 2.0 root hub

Device I wanted to analyze was at bus 3, port 3. This knowledge allowed me to check its power level.

cat /sys/bus/usb/drivers/usb/3-3/power/level

Value on that was returned immediately confirmed what I suspected. The port standby was disabled. Another test was to write auto inside the same file and current consumption fell down after 2-3 seconds (essentially, USB standby timeout), as expected.

Unfortunately, as soon as the device was plugged back in, it reset its behavior to always-on. Well, udev rules to the rescue.

Only, I already had FTDI udev rules present on my system (/etc/udev/rules.d/99-libftdi.rules). It seems that one of the tools I used (ftdidev library being the prime suspect) already installed some rules. Any rules I add to that file might get overwritten eventually. So, I decided to create a new file for both FT232R and X Series chips.

cat << EOF | sudo tee /etc/udev/rules.d/42-ftdi.rules
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTR{power/control}:="auto"
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", ATTR{power/control}:="auto"
EOF

This will turn on auto standby handling for my FTDI devices while making sure that subsequent rule doesn’t overwrite it (thus :=). Exactly what I needed to make my laptop battery happy.


To apply this without reboot, you can use the following command:

sudo udevadm control --reload-rules && sudo udevadm trigger

Reading DDR4 SPD Information on Supermicro M11SDV

Viewing DIMM SDP data under Linux is usually a trivial affair. On my Framework (gen 11) laptop, this went something like this:

sudo apt install i2c-tools
sudo modprobe eeprom
sudo modprobe i2c-i801
sudo decode-dimms

However, on my Supermicro M11SDV server with AMD Epyc 3000 processor, this didn’t work. No matter what I did, nothing would show. After messing around a bit, I found the solution on Unraid forum.

First, I needed to find where is SPD actually listening. The easiest way to determine this is to first list all I2C buses and then search for devices between I2C addresses 0x50 and 0x57:

sudo i2cdetect -l
sudo i2cdetect -y 0 0x50 0x57
sudo i2cdetect -y 1 0x50 0x57
sudo i2cdetect -y 2 0x50 0x57

For my motherboard, I actually found output on bus number 2. It looked something like this:

Warning: Can't use SMBus Quick Write command, will skip some addresses
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:
10:
20:
30:
40:
50: 50 51 52 53 -- -- -- --
60:
70:

Once the bus is found, we just need to load the ee1004 module that works on AMD board. This has to be repeated for each I2C address that was found. Do note that the path contains the I2C bus number (in my case i2c-2).

sudo modprobe ee1004
echo ee1004 0x50 | sudo tee /sys/bus/i2c/devices/i2c-2/new_device
echo ee1004 0x51 | sudo tee /sys/bus/i2c/devices/i2c-2/new_device
echo ee1004 0x52 | sudo tee /sys/bus/i2c/devices/i2c-2/new_device
echo ee1004 0x53 | sudo tee /sys/bus/i2c/devices/i2c-2/new_device

And now finally we can run decode-dimms again and read all that lovely SPD data.