<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Christian Kastner</title><link>https://www.kvr.at/</link><description>Christian Kastner</description><atom:link href="https://www.kvr.at/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:ckk@kvr.at"&gt;Christian Kastner&lt;/a&gt; </copyright><lastBuildDate>Sun, 15 Mar 2026 21:21:17 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Radxa Orion O6 - finally an arm64 board I'm happy with</title><link>https://www.kvr.at/posts/radxa-orion-o6-finally-an-arm64-board-im-happy-with/</link><dc:creator>Christian Kastner</dc:creator><description>&lt;p&gt;The &lt;a class="reference external" href="https://radxa.com/products/orion/o6/"&gt;Radxa Orion O6&lt;/a&gt; seems to be the
arm64 device I've always wanted. Finally!&lt;/p&gt;
&lt;p&gt;Because it supports UEFI boot, Debian can be installed with the vanilla
installation media. Neither custom images, nor additional firmware, nor any
other tricks were required on my end. In fact, the entire process was
indistinguishable from the amd64 installations I've been accustomed to for
two decades: just plug in a USB flash drive, boot, and install.&lt;/p&gt;
&lt;p&gt;The system's specs are pretty neat. The CPU is ARMv9.2, though without SME,
which would have been useful for debugging packages that use it in some way.
It's also rich in interfaces, including a Gen4 PCIe x16 slot (with 8 lanes),
which enables me to run tests utilizing GPUs. The BIOS version the board came
with had a bug where WiFi was permanently disabled, but this has been
fixed in the meantime. The only odd thing was the socket for the RTC battery:
the CR1220 it requires seems to be rather niche: none of the hardware stores I
visited carried it, so I had to order one online.&lt;/p&gt;
&lt;p&gt;I installed &lt;a class="reference external" href="https://www.debian.org/releases/stable/"&gt;Debian trixie&lt;/a&gt; to a
NVMe drive, and for 6 months now, it has been running flawlessly. I've been
using this board as a development and debugging system for arm64 optimizations,
in particular for &lt;a class="reference external" href="https://tracker.debian.org/ggml"&gt;ggml&lt;/a&gt;'s feature-specific
arm64 backends which are hard to debug on our porterboxes. However, the host
will soon transition to a general CI worker for AI/ML related packages, similar
to the &lt;a class="reference external" href="https://ci.rocm.debian.net"&gt;ROCm CI&lt;/a&gt; we've built for testing packages
utilizing AMD GPUs.&lt;/p&gt;
&lt;p&gt;I'll post more about this new CI soon.&lt;/p&gt;
&lt;p&gt;Purchase of this board was funded by the Debian Project, and was thus enabled
through your &lt;a class="reference external" href="https://www.debian.org/donations"&gt;donations&lt;/a&gt;.&lt;/p&gt;</description><guid>https://www.kvr.at/posts/radxa-orion-o6-finally-an-arm64-board-im-happy-with/</guid><pubDate>Sun, 15 Mar 2026 20:17:41 GMT</pubDate></item><item><title>Simple-PPA, a minimalistic PPA implementation</title><link>https://www.kvr.at/posts/simple-ppa-a-minimalistic-ppa-implementation/</link><dc:creator>Christian Kastner</dc:creator><description>&lt;p&gt;Today, the Debusine developers launched
&lt;a class="reference external" href="https://www.freexian.com/blog/debusine-repositories-beta/"&gt;Debusine repositories&lt;/a&gt;,
a beta implementation of PPAs. In the announcement, Colin remarks that
&lt;em&gt;"[d]iscussions about this have been happening for long enough that people started
referring to PPAs for Debian as 'bikesheds'"&lt;/em&gt;; a characterization that I'm sure
most will agree with.&lt;/p&gt;
&lt;p&gt;So it is with great amusement that on this same day, I launch a second PPA
implementation for Debian: &lt;a class="reference external" href="https://simple-ppa.debian.net"&gt;Simple-PPA&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Simple-PPA was never meant to compete with Debusine, though. In fact, it's
entirely the opposite: from discussions at DebConf, I knew that it was only a
matter of time until Debusine gained a PPA-like feature, but I needed a
stop-gap solution earlier, and with some polish, what was once by Python script
already doing APT processing for
&lt;a class="reference external" href="https://apt.ai.debian.net"&gt;apt.ai.debian.net&lt;/a&gt;, recently became Simple-PPA.&lt;/p&gt;
&lt;p&gt;Consequently, Simple-PPA lacks (and will always lack) all of the features that
Debusine offers: there is no auto-building, no CI, nor any other type of QA.
It's the simplest possible type of APT repository: you just upload packages,
they get imported into an archive, and the archive is exposed via a web server.
Under the hood, &lt;a class="reference external" href="https://packages.debian.org/sid/reprepro"&gt;reprepro&lt;/a&gt; does all
the heavy lifting.&lt;/p&gt;
&lt;p&gt;However, this also means it's trivial to set up. The following is the entire
configuration that
&lt;a class="reference external" href="https://simple-ppa.debian.net"&gt;simple-ppa.debian.net&lt;/a&gt; started with:&lt;/p&gt;
&lt;pre class="literal-block"&gt;# simple-ppa.conf

[CORE]
SignWith = 2906D748B7551BC8
ExportDir = /srv/www/simple-ppa
MailFrom: Simple-PPA &amp;lt;admin@simple-ppa.debian.net&amp;gt;
Codenames = sid forky trixie trixie-backports bookworm bookworm-backports
AlsoAllow = forky: unstable
            trixie: unstable
            bookworm: unstable

[simple-ppa-dev]
Label = Simple-PPA's self-hosted development repository
# ckk's key
Uploaders = allow * by key E76004C5CEF0C94C+

[ckk]
Label = Christian Kastner at Simple-PPA
Uploaders = allow * by key E76004C5CEF0C94C+&lt;/pre&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;CORE&lt;/code&gt; section just sets some defaults and sensible rules. Two PPAs are
defined, &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;simple-ppa-dev&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal"&gt;ckk&lt;/code&gt;, which accept packages signed by the
key with the ID &lt;code class="docutils literal"&gt;E76004C5CEF0C94C&lt;/code&gt;. These PPAs use the global defaults, but
individual PPAs can override &lt;code class="docutils literal"&gt;Architectures&lt;/code&gt;, &lt;code class="docutils literal"&gt;Suites&lt;/code&gt;, and &lt;code class="docutils literal"&gt;Components&lt;/code&gt;,
and of course allow an arbitrary number of users.&lt;/p&gt;
&lt;p&gt;Users upload to this archive using SFTP (e.g.: with
&lt;a class="reference external" href="https://packages.debian.org/sid/dput-ng"&gt;dput-ng&lt;/a&gt;). Every 15 minutes,
uploads get processed, with ACCEPTED or REJECTED mails sent to the Maintainer
address. The APT archive of all PPAs is signed with a single global key.&lt;/p&gt;
&lt;p&gt;I myself intend to use Debusine repositories soon, as the autobuilding and
the QA tasks Debusine offers are something I need. However, I do still see a
niche use case for Simple-PPA: when you need an APT archive, but don't want to
do a deep dive into &lt;code class="docutils literal"&gt;reprepro&lt;/code&gt; (which is extremely powerful).&lt;/p&gt;
&lt;p&gt;If you'd like to give Simple-PPA a try, head over to
&lt;a class="reference external" href="https://simple-ppa.debian.net"&gt;simple-ppa.debian.net&lt;/a&gt; and follow the
instructions for users.&lt;/p&gt;</description><guid>https://www.kvr.at/posts/simple-ppa-a-minimalistic-ppa-implementation/</guid><pubDate>Tue, 16 Dec 2025 21:15:05 GMT</pubDate></item><item><title>Easy dynamic dispatch using GLIBC Hardware Capabilities</title><link>https://www.kvr.at/posts/easy-dynamic-dispatch-using-GLIBC-hardware-capabilities/</link><dc:creator>Christian Kastner</dc:creator><description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; With GLIBC 2.33+, you can build a shared library multiple times
targeting various optimization levels, and the dynamic linker/loader will pick
the highest version supported by the current CPU. For example, with the layout
below, on a Ryzen 9 5900X, &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;x86-64-v3/libfoo0.so&lt;/span&gt;&lt;/code&gt; would be loaded:&lt;/p&gt;
&lt;pre class="literal-block"&gt;/usr/lib/glibc-hwcaps/x86-64-v4/libfoo0.so
/usr/lib/glibc-hwcaps/x86-64-v3/libfoo0.so
/usr/lib/glibc-hwcaps/x86-64-v2/libfoo0.so
/usr/lib/libfoo0.so&lt;/pre&gt;
&lt;section id="longer-version"&gt;
&lt;h2&gt;Longer Version&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://manpages.debian.org/unstable/manpages/ld.so.8.en.html#glibc_Hardware_capabilities_(from_glibc_2.33)"&gt;GLIBC Hardware Capabilities&lt;/a&gt;
or "hwcaps" are an easy, almost trivial way to add
a simple form of dynamic dispatch to any amd64 or POWER build, provided that
either the build target or the compiler's optimizations can make use of
certain CPU extensions.&lt;/p&gt;
&lt;p&gt;Mo Zhou pointed me towards this
when I was faced with the challenge of creating a performant Debian package for
&lt;a class="reference external" href="https://github.com/ggml-org/ggml"&gt;ggml&lt;/a&gt;,
the tensor library behind
&lt;a class="reference external" href="https://github.com/ggml-org/llama.cpp"&gt;llama.cpp&lt;/a&gt;
and
&lt;a class="reference external" href="https://github.com/ggml-org/whisper.cpp"&gt;whisper.cpp&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;In the meantime, ggml has extended its own plugin-like support for dynamic
dispatch to other CPU architectures, the lack of which originally motivated
this work.&lt;/p&gt;
&lt;p&gt;The Debian package has been switched to this new approach. For reference,
the last package version implementing the hwcaps approach was tagged
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;debian/0.0_git20250712.d62df60-1&lt;/span&gt;&lt;/code&gt;, browseable
&lt;a class="reference external" href="https://salsa.debian.org/deeplearning-team/ggml/-/tree/debian/0.0_git20250712.d62df60-1"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="the-challenge"&gt;
&lt;h2&gt;The Challenge&lt;/h2&gt;
&lt;p&gt;A performant yet universally loadable library needs to make use of some
form of dynamic dispatch to leverage the most effective SIMD extensions
available on any given CPU it may run on. Last January, when I first started
with the packaging of ggml for Debian, ggml did have support for this through
its &lt;code class="docutils literal"&gt;GGML_CPU_ALL_VARIANTS=ON&lt;/code&gt; option, but this was limited to amd64.
This meant that on all the other architectures that Debian supports, I would
need to target some ancient
&lt;a class="reference external" href="https://wiki.debian.org/ArchitectureSpecificsMemo#Architecture_baselines"&gt;baseline&lt;/a&gt;,
thus effectively crippling the package there.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dynamic-dispatch-using-hwcaps"&gt;
&lt;h2&gt;Dynamic Dispatch using hwcaps&lt;/h2&gt;
&lt;p&gt;hwcaps were introduced in GLIBC 2.33 and replace the (now) Legacy Hardware
Capabilities, which were removed in 2.37. The way hwcaps work is delightfully
simple: the dynamic linker/loader will look for a shared library not just in
the standard library paths, but also in subdirectories
thereof of the form &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;hwcaps/&amp;lt;level&amp;gt;&lt;/span&gt;&lt;/code&gt;, starting with the highest &lt;code class="docutils literal"&gt;&amp;lt;level&amp;gt;&lt;/code&gt;
that the current CPU supports. The levels are predefined. I'm using the
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels"&gt;amd64&lt;/a&gt; levels
below.&lt;/p&gt;
&lt;p&gt;For ggml, this meant that I simply could build the library in multiple passes,
each time targeting a different &lt;code class="docutils literal"&gt;&amp;lt;level&amp;gt;&lt;/code&gt;, and install the result in the
corresponding subdirectory, which resulted in the following
layout (reduced to &lt;code class="docutils literal"&gt;libggml.so&lt;/code&gt; for brevity):&lt;/p&gt;
&lt;pre class="literal-block"&gt;/usr/lib/x86_64-linux-gnu/ggml/glibc-hwcaps/x86-64-v4/libggml.so
/usr/lib/x86_64-linux-gnu/ggml/glibc-hwcaps/x86-64-v3/libggml.so
/usr/lib/x86_64-linux-gnu/ggml/glibc-hwcaps/x86-64-v2/libggml.so
/usr/lib/x86_64-linux-gnu/ggml/libggml.so&lt;/pre&gt;
&lt;p&gt;In practice, this means that on a CPU supporting AVX512, the linker/loader
would load &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;x86-64-v4/libggml.so&lt;/span&gt;&lt;/code&gt; if it existed, and otherwise continue to
look for the other levels, all the way down to the lowest one. On a
CPU which supported only SSE4.2, the lookup process would be the same,
ending with picking &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;x86-64-v2/libggml.so&lt;/span&gt;&lt;/code&gt;.
With QEMU, all of this was quickly
&lt;a class="reference external" href="https://lists.debian.org/debian-ai/2025/02/msg00005.html"&gt;verified&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that the lowest-level library, targeting &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;x86-64-v1&lt;/span&gt;&lt;/code&gt;, is not installed
to a subdirectory, but to the path where the library would normally have been
installed. This has the nice property that on systems not using
GLIBC, and thus not having hwcaps available, package installation will still
result in a loadable library, albeit the version with the worst performance.
And a careful observer might have noticed that in the example above, the
library is installed to a private &lt;code class="docutils literal"&gt;ggml/&lt;/code&gt; directory, so this mechanism also
works when using &lt;code class="docutils literal"&gt;RUNPATH&lt;/code&gt; or &lt;code class="docutils literal"&gt;LD_LIBRARY_PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As mentioned above, Debian's ggml package will soon switch to
&lt;code class="docutils literal"&gt;GGML_CPU_ALL_VARIANTS=ON&lt;/code&gt;, but this was still quite the useful feature to
discover.&lt;/p&gt;
&lt;/section&gt;</description><guid>https://www.kvr.at/posts/easy-dynamic-dispatch-using-GLIBC-hardware-capabilities/</guid><pubDate>Sat, 12 Jul 2025 18:20:57 GMT</pubDate></item><item><title>15th Anniversary of My First Debian Upload</title><link>https://www.kvr.at/posts/15th-anniversary-of-my-first-debian-upload/</link><dc:creator>Christian Kastner</dc:creator><description>&lt;p&gt;Time flies! 15 years ago, on 2010-03-18, my first upload to the Debian
archive was &lt;a class="reference external" href="https://tracker.debian.org/news/246978/accepted-zd1211-firmware-22100-1-source-all"&gt;accepted&lt;/a&gt;.
Debian had replaced Windows as my primary OS in 2005, but it was only when I
saw that package
&lt;a class="reference external" href="https://tracker.debian.org/pkg/zd1211-firmware"&gt;zd1211-firmware&lt;/a&gt; had been
orphaned that I thought of becoming a contributor. I owned a Zyxel G-202 USB
WiFi fob that needed said firmware, and as is so often is with open-source
software, I was going to scratch my own itch. Bart Martens thankfully helped
me adopt the package, and sponsored my upload.&lt;/p&gt;
&lt;p&gt;I then joined Javier Fernández-Sanguino Peña as a cron maintainer and upstream,
and also worked within the Debian Python Applications, Debian Python Modules,
and Debian Science Teams, where Jakub Wilk and Yaroslav Halchenko were kind
enough to mentor me and eventually support my application to become a
Debian Maintainer.&lt;/p&gt;
&lt;p&gt;Life intervened, and I was mostly inactive in Debian for the next two years.
Upon my return in 2014, I had Vincent Cheng to thank for sponsoring most of
my newer work, and for eventually supporting my application to become a
Debian Developer. It was around that time that I also attended my first
DebConf, in &lt;a class="reference external" href="https://debconf14.debconf.org/"&gt;Portland&lt;/a&gt;, which remains one
of my fondest memories. I had never been to an open-source software conference
before, and DebConf14 really knocked it out of the park in so many ways.&lt;/p&gt;
&lt;p&gt;After another break, I returned in 2019 to work mostly on Python and machine
learning libraries. In 2020, I finally completed a process that I had first
started in 2012 but had never managed to finish before: converting cron from
&lt;a class="reference external" href="https://manpages.debian.org/unstable/dpkg-dev/dpkg-source.1.en.html#Format:_1.0"&gt;source format 1.0&lt;/a&gt;
(one big diff) to
&lt;a class="reference external" href="https://manpages.debian.org/unstable/dpkg-dev/dpkg-source.1.en.html#Format:_3.0_(quilt)"&gt;source format 3.0 (quilt)&lt;/a&gt;
(a series of patches). This was a process where I converted 25 years worth of
organic growth into a minimal series of logically grouped changes (more
&lt;a class="reference external" href="https://sources.debian.org/src/cron/3.0pl1-137/debian/source-format-3.md/"&gt;here&lt;/a&gt;).
This was my white whale.&lt;/p&gt;
&lt;p&gt;In early 2023, shortly after the launch of ChatGPT which triggered an
unprecedented AI boom, I started contributing to the Debian ROCm Team, where
over the following year, I bootstrapped our CI at
&lt;a class="reference external" href="https://ci.rocm.debian.net"&gt;ci.rocm.debian.net&lt;/a&gt;. Debian's current tooling
lack a way to express dependencies on specific hardware other than CPU ISA,
nor does it have the means to run autopkgtests using such hardware. To get
autopkgtests to make use of AMD GPUs in QEMU VMs and in containers, I had to
fork autopkgtest, debci, and a few other components, as well as create a fair
share of new tooling for ourselves. This worked out pretty well, and the CI
has grown to support 17 different AMD GPU architectures. I will share more
on this in upcoming posts.&lt;/p&gt;
&lt;p&gt;I have mentioned a few contributors by name, but I have countless others to
thank for collaborations over the years. It has been a wonderful experience,
and I look forward to many years more.&lt;/p&gt;</description><guid>https://www.kvr.at/posts/15th-anniversary-of-my-first-debian-upload/</guid><pubDate>Tue, 18 Mar 2025 23:16:16 GMT</pubDate></item></channel></rss>