Category Archives: NAS

KX-7000 on AOS-MMZX700D

I was interested in buying the latest Zhaoxin KX-7000 CPU for quite some time, which I see as a modified variation of the last unreleased Centaur CNS CPU. It is not exactly the same, but both were definitely designed in some kind of collaborative fashion. It will likely be the last one, since the Centaur team has now been disbanded.

There were a few options in the past, mainly desktop-oriented motherboards, but their prices were too high for me to consider (and a desktop motherboard is not what I was looking for). Luckily, more options appeared recently, and I got lucky enough to buy Aiostar AOS-MMZX700D (AOS-MMZX7000D on the motherboard sticker) in the Mini-ITX form-factor for a pretty decent price. My variant is very slightly customized compared to the default configuration, having one VGA connector and one HDMI connector instead of two HDMI ports.

Performance-wise, the KX-7000 CPU is not very impressive compared to the competition, and even the motherboard manufacturer admits this in the product description, but it is much more capable than anything before it and pretty capable overall (based on the resources available online). The motherboard is positioned more toward embedded/industrial usage (for example, it doesn’t have an actual PCIe slot, uses a DC-IN power adapter, and provides a SIM slot). However, I personally am less interested in its positioning and performance. The strongest reason was to continue improving Centaur support in NetBSD itself, and probably utilize the motherboard for internal purposes, such as upgrading my NAS server, or having a decent and compact AMD64 testing platform if successful.

And indeed, this platform has provided quite a number of various things to fix and improve; some easy, some still unresolved. I will go through some of them in this article!

Desired number of cache colors is not even

The first issue surfaced on the first boot of the NetBSD operating system. I hit a relatively early panic in the boot process that was saying, “desired number of cache colors 21 is > 1, but not even!” The error message was pointing to this line.

Page coloring is how the kernel groups physical pages by which cache sets they map to, so pages with the same “color” compete for the same cache lines. The kernel calculates the number of colors as cache size / (page size × associativity). So something was off in this calculation.

Fortunately, this mystery was relatively easy to crack and revealed two problems in the VIA/Zhaoxin CPU cache identification. The first one is a fairly longstanding issue, and in theory it affected all VIA CPUs starting with Nehemiah. The other one is more recent and is related to the way cache parameters should be decoded in general.

The uneven number was caused by the first issue. Historically, L2 cache parameters are decoded using the 0x80000006 CPUID function. Certain bits encode the cache size, line size, and associativity. This matches the AMD way of doing the same thing for the L2 cache.

However, the NetBSD implementation had a subtle difference between AMD and Centaur when decoding cache associativity. AMD was using a lookup to resolve the value extracted from the bits, while Centaur was using the raw value directly. This difference was likely backed up by VIA datasheets, which never mentioned any lookup table and described the bits as containing the actual value.

In practice, it turned out that they also store the same value as AMD, meaning that a lookup table is required to obtain the actual value!

This issue had never been noticed because the number of colors happened to still be a power of 2, even if it was calculated incorrectly for certain models. It is the KX-7000, which has 8-way L2 associativity, that finally exposed the issue, since 6 is the raw value and the calculation therefore failed to pass the power-of-2 check.

The second issue affected newer VIA (CNQ+) and most, if not all, Zhaoxin CPUs. It appeared that, at some point, it was decided to switch to Intel’s way of encoding cache parameters through CPUID leaf 4 instead. The parameters mostly match between the two methods (though there were cases where they disagreed), but it became the preferred way of obtaining this information. Additionally, the L3 cache is encoded only this way in the KX-7000 CPU, so CPUID leaf 4 needs to be used to obtain its L3 cache parameters.

The fix can be described in two steps:

  1. Use the same cache-associativity lookup table as AMD.
    The raw associativity value reported by the CPU needs to be translated using the lookup table instead of being treated as the final value.
  2. Parse cache information from CPUID leaf 4 when available.
    Newer VIA and Zhaoxin CPUs use the Intel-style CPUID leaf 4 to report cache parameters, including the L3 cache on the KX-7000. The code therefore uses leaf 4 to obtain the cache information when it is available.

Same changes were applied to cpuctl(8) utility as well.

SMAP and PMAP

KX-7000 supports SMAP feature designed to set user-space mapping to prevent supervisor access to those mapping would cause a trap. Once I fixed the issue above, the boot process still failed and hit page fault trap in pmap_enter_ma() somewhere before login screen was supposed to appear. Quite quickly I identified that SMAP feature is causing this, thus I committed a workaround to disable it for the Zhaoxin CPUs (it is currently supported at least by KX-7000 and KX-6000G, and maybe KX-6900). I was hoping to quickly identify the root cause, however I haven’t reached this stage yet. Later I identified that it affects only newly installed pmap pages and PTE entries. I found that invlpg instruction (invalidating TLB caches) in couple places allows the system boot properly, but those places can affect performance, and not necessarily correct, thus I couldn’t apply them. There are few more things which affect the outcome, but none solve the problem. So far, this issue is not fully resolved, but disabling SMAP helps and this is currently applied. For whatever reason i386 is not affected in the same way and boots successfully (either SMAP is not used or recursive mapping is more shallow than the one in amd64). OpenBSD seems to be affected by this problem too. FreeBSD boots successfully though.

WARNING: no TOD clock present

After disabling SMAP, the system finally booted fully! However, I immediately noticed a warning at the end of the boot process: “WARNING: no TOD clock present”, followed by a couple more related warnings. After some investigation, it turned out to be related to a quite recent change for virtualized environments, specifically QEMU microvm boot, which needed a check that the RTC (real-time clock) is enabled, as otherwise the boot would hang. However, that check was not valid for real hardware. It used the MC146818 Register D bits to see whether bits 0-6 were zero, but real hardware may use those bits for the date alarm, so they are not necessarily zero. This is not specific to the KX-7000: any machine with non-zero values there could have its RTC mistakenly not attached. The KX-7000 system just happened to be one such case. The fix restricts the check to VM guest environments.

Legacy boot issue

The motherboard has an option to use either EFI boot or legacy BIOS boot. Oddly, it doesn’t allow both: one must be chosen in the BIOS setup (other motherboards can typically support both without a specific switch). Nevertheless, I tried to boot NetBSD using BIOS boot (specifically the i386 and amd64 BIOS-only images). Both failed to load the kernel, ending in “read section: input/output error” or “inappropriate file type or format” errors, or even an abrupt reboot. Experience from the VIA C7 boot issue investigation helped me get into bootloader debugging fairly quickly. After several sessions I found that forcing the INT13 extensions resolves the problem (#define FORCE_INT13EXT can be used to build a bootloader that enforces this). The INT13 extensions provide LBA-based disk access, while NetBSD uses CHS (Cylinder, Head, Sector) addressing for reads up to a certain sector and only switches to LBA beyond what CHS can address. For some reason, the CHS approach doesn’t work properly on the KX-7000. I proposed a fix to always use the INT13 extensions if the BIOS reports supporting them, but the developers rejected it, because some other legacy systems may have the opposite problem (support is reported but doesn’t work, or a SCSI ROM doesn’t support it at all). I plan to continue with a different approach, probably reading a certain sector and checking whether LBA can be used, but it is not ready yet. For now, the only option is to enforce the extensions, which requires rebuilding the bootloader. OpenBSD and FreeBSD don’t have this issue, since they don’t do CHS reads if INT13 extensions are supported.

HDMI output issue on BIOS boot

The motherboard has both VGA and HDMI outputs, and I typically use VGA for testing. However, once I managed to boot into the system using BIOS boot, I tried switching to HDMI output instead, and in that case the boot hangs in a not fully deterministic way, meaning not always on the same line. I tried disabling various devices and disabling vesa, which lets the boot move further, but it never completes. OpenBSD is potentially affected too: its kernel boots fully, but it hangs while starting userland services (which doesn’t happen with VGA output). It looks like a memory, framebuffer, or timeout issue, but I have little idea how to investigate it. EFI boot is not affected in the same way and boots without problems even when HDMI output is used.

Other smaller improvements

Besides the improvements above, I also committed a couple more changes related to VIA/Zhaoxin CPUs. Specifically, I added the Centaur vendor ID and family 7 to the invariant TSC check (needed for the KX-7000), which is performed through the CPUID 0x80000007 function. Additionally, I added a check for using lfence to serialize rdtsc on all CPUs from VIA Nano onwards, including all Zhaoxin CPUs. I also updated the pcidevs and hdaudiodevs files to include the PCI IDs and HD audio device IDs from the KX-7000 (and other VIA/Zhaoxin) CPUs. Finally, I added an extended model check for family 7 CPUs, which are used only by Zhaoxin. This allows more accurate model reporting in cpuctl(8) and in kernel checks.

One more issue with BIOS boot is that consdev com0 / consdev com1 does not redirect the console to the serial port. I haven’t investigated this yet.

I will continue working on the platform improvements, but the pace will likely slow down now. The initial support is in place, and the workarounds needed to boot the system have been identified or fixed, which makes for a stable and usable system as is. I am requesting, or planning to request, pull-ups of most of these changes to at least NetBSD 11, and some of them have already been applied.

NetBSD NAS software setup

Once boot process was solved, I needed to setup ZFS for my hard drives and several services like NFS and Samba, setup network bonding using the new dedicated lagg(4) interface with LACP protocol, which in turn would complete the major NAS functionality.

ZFS setup

Initial ZFS setup was pretty easy. Luckily, configuring ZFS modules (solaris, zfs) in modules.conf(5) file was enough to make it work, even if configuration file was located in chrooted root partition. I mainly followed the Creating ZFS pools on NetBSD article from the UnitedBSD , or more exactly the “Create a ZFS pool” section by creating a ZFS pool and datasets with the mount points. After that, adding “zfs=YES” line in rc.conf(5) file does the final trick in order to mount ZFS partitions in their intended locations. I also enabled de-duplication and gzip compression features. The main mistake followed after adding a second hard drive to the zpool, which was intended to be a mirrored drive, for which attach command should have been used. NetBSD’s zpool command doesn’t allow removal of the drive without proper redundancy and doesn’t support forced removal, thus I need to recreate the pool, which was a bit of painful setback. According to recommendations I scheduled once a month zpool scrub operation, but still need to setup scheduled snapshots in the nearest future.

Link aggregation

As mentioned previously QNAP TVS-675 system contains two expansion card slots using PCIe Gen3 x4 interface. Thanks to the cut at the back of the port, cards with x8 physical slot can also fit into the port (longer cards may not fit though without modifications, metal frame can block the insertion). The challenge in doing so that it was a very short distance between PSU and a slot bracket, which made it very difficult to unscrew it without removing the PSU first. Once the network card was physically in place, the major problem occurred while setting up the lagg(4) interface using link aggregation control protocol (LACP). It appeared that mcx(4) driver wasn’t setting up full-duplex data transmission required by the protocol. Fortunately, the fix was relatively easy and made it into NetBSD 10 release by enforcing full-duplex in the driver. Additionally, missing lagg parameters were documented in ifconfig(8) man page. After the fix, network bonding setup was pretty straightforward by creating /etc/ifconfig.lagg0 file:

create
laggproto lacp laggport mcx0 laggport mcx1
up

I also created ifconfig.mcx0 and ifconfig.mcx1 with only up command and added net_interfaces=”mcx0,mcx1,lagg0″ line to /etc/rc.conf to control initialization order and ensure that mcx interfaces are up before lagg0 interface is created and initialized. For dhcpcd I added dhcpcd_flags=”-qM lagg0″ flags. After reboot my switch was happy to accept link aggregation with my NAS system.

Samba setup

Next step was to install and enable samba(8). The small issue was the missing xbase set required by the samba installation, since I was not aware of this requirement. I simply downloaded the required set and extracted it manually. Other than that I could mainly reuse configuration file from Linux system by only adjusting required paths and placing it in /usr/pkg/etc/samba/smb.conf location. Of course I needed to create a samba user (smbpasswd). Also worth to note that smbd, nmbd and winbindd services should be enabled instead of samba service, contrary to typical Linux experience.

NFS setup

NFS configuration was not compatible with the Linux one on the other hand and I needed to rewrite it from scratch. NetBSD “how to set up nfs and nis” and UnitedBSD NFS on NetBSD: server and client side wiki articles were a great guidance. To enable NFS properly I needed to add these line to /etc/rc.conf:

# NFS server
rpcbind=YES
nfs_server=YES
nfsd_flags=’-n8′
mountd=${nfs_server}
lockd=${nfs_server}
statd=${nfs_server}

And configure /etc/exports according to examples from the articles above. From client side I just needed to adjust /etc/fstab to the new NFS path.

Other notes

I also installed rtorrent as my torrent service and utilized cron to enable it after boot by adding this line to crontab configuration:

@reboot /usr/bin/tmux new-session -d '/usr/pkg/bin/rtorrent -D -I -n -o import=$
PWD/.rtorrent.rc'

.rtorrent.rc file just need to point to the session path, otherwise I kept it standard. After that tmux attach -d -t 0 command can be used to attach to the rtorrent session and CTRL+B D to detach from it.

In order to monitor HDD and its temperature I installed smartmontools utility. Current temperature can be retrieved using sudo smartctl -a /dev/rwd1|grep -i temp command (raw device should be used, thus rwd1 instead of wd1).

Finally I created the patch for the CPU temperature which didn’t make it to NetBSD 10 release, but hopefully will be available with NetBSD 10.1 through envstat(8) command. For time being I am using the patched kernel.

Overall my experience is quite positive with the new setup. There are still few improvements required, mainly for the data redundancy and potentially better power management and improved CPU support. The NAS device doesn’t like enclosed environments without proper airflow and my become extremely noisy in such case, however it works pretty silently be default. Actually the main noise was coming from 10-Gbit ethernet card, which has an active fan. Hard drives are also pretty audible unfortunately, thus SSDs would be preferred if silence is the priority.

Boot into SSD in QNAP TVS-675

Historically, my first NAS system was running NetBSD operating system (5th release if I am not mistaken), however after a hardware update, I was forced to switch to the Debian Linux due to hardware support issues at that time. Since then I have used few different distributions for over a decade (mainly Arch and Artix). Several years ago I upgraded the underlying platform again with the hopes to use it for at least as long as my previous system. This NAS setup is based on Biostar FX9830M motherboard. My goal in doing so is to make setup as cheap as possible by scrapping various parts from my closet. Unfortunately, it didn’t workout as planned, mainly due to uncomfortably noisy operation of aforementioned motherboad’s CPU fan, which I have never managed to remedy (unusual fan connector and cooling solution is a limiting factor, requiring to do custom solutions without knowing if that solution will work). In addition, the compact case didn’t provide enough airflow leading to slightly overheating HDDs which may affect their operational lifetime. Thus, I decided that it may be a good chance to come back to a long time dream of switching back to NetBSD in my NAS setup and I acquired QNAP TVS-675 NAS system for this purpose.

Under the hood it uses Zhaoxin KaiXian KX-U6580 8-core x86-64 CPU, has 6 HDD bays, has an option to add two PCIe Gen 3 x4 cards (the slot allows to attach longer cards thanks to the cut at the back of it), and supports NVMe expansion slots. It is almost a typical x86-64 PC, just wrapped into a “fancy” case specifically designed for NAS purposes. It operates pretty silently by default, with only audible noise coming from HDDs themselves during heavy operations (once high capacity SSDs are cheap enough I may switch to them).

The installation of the NetBSD 10.0 didn’t bring any surprises and went smoothly, however I faced the first issue just after the first reboot. There was no support to boot directly from SATA SSD (limitation of the system’s BIOS). Only the USB boot devices are supported. As a result, I started to look into options somehow to start booting from USB, to switch the root system to the SSD partition at the subsequent stages of the boot process. After reading a few resources (will mention them at the end of the article) and consulting the NetBSD developers, I decided to use a pretty simple approach with SSD root partition being mounted to /altroot, where kernel eventually is being instructed to chroot to it. The approach is similar – to root on ZFS or CGD functionality, however I didn’t configure a separate ramdisk for that, at least now.

In order to achieve that, I needed to prepare a bootable USB flash drive. One can use a lengthy instructions with the UEFI in mind, or install a minimal system using installation media, however I took a lazy approach by dissecting provided installation media itself. I just mounted it on my main NetBSD system and deleted all unnecessary folders and files, updated netbsd kernel (I am building a custom one, but GENERIC can be used) and updated /etc/rc script by mounting required partitions, including SSD root to /altroot and finally setting init.root kernel option to /altroot, so that it would switch to it during boot process:

PATH=/sbin:/bin
export PATH

mount -u /
mount -t tmpfs tmpfs /tmp
mount /altroot
sysctl -w init.root=/altroot

# done, move on to multiuser mode
exit 0

To complete a setup, I added required fields to /etc/fstab by setting correct /altroot mount point to the SSD’s root partition.

NAME=<usbpartionid>		/		ffs	rw		1 1
tmpfs			/tmp		tmpfs	rw		0 0
NAME=<ssdpartitionid>			/altroot	ffs	ro,log	1 1

The important part was to mount /altroot as read-only (ro), otherwise automatic filesystem check kicked-in and disrupted the boot process (it can’t check rw mounted partitions), requiring for the manual interaction. It is not an issue, since root partition will remount in rw mode by reading SSDs /etc/fstab. If there’s a need to change some files in /altroot before chroot (like rndseed for example), automount can be still rw but rc script should remount /altroot manually as read only shortly before sysctl command. Alternative approach may be to skip remounting root partition at all, but I haven’t tried it.

After reboot the system successfully booted and switched to SSD root folder!

Ideally I would like to avoid having USB flash drive being attached at all. I believe that integrated USB DISK module can be utilized somehow, but I haven’t explored that just yet, since i I don’t want to mess up the default boot configuration for the their own custom OS and it needs more time and skills to implement. For the time being, I would likely buy a very short USB flash driver, similar to Intenso Slim Line to make protrusion from the case as minimal as it can be.

The next step will be to setup my hard drives to using ZFS file system, setup NFS and samba as well as network with lagg(4) link aggregation. I am unsure at the moment, where I should load zfs modules, should it be before the chroot operation happens or it can be loaded after. Currently I am utilizing SSD root partition’s /etc/modules.conf file, which loads modules correctly, however I am not certain if it won’t be too late for mounting ZFS partitions automatically. I will definitely explore that in the next post.

Few useful resources on this topic:
HowTo: Making a bootable ISO w/ miniroot.kmod etc.
Building a NetBSD ramdisk kernel
Root On ZFS
NetBSD Full-Disk Encryption with CGD
Building NetBSD system components