packaging: ship DKMS modules in their own binary packages - #123
Open
Christopher Obbard (obbardc) wants to merge 7 commits into
Open
Christopher Obbard (obbardc) wants to merge 7 commits into
Christopher Obbard (obbardc) wants to merge 7 commits into
Conversation
The image ships /boot/vmlinuz-<kver> and /boot/config-<kver> but not
System.map. That is not just a missing convenience file: the depmod snippet
debhelper generates for every package shipping a kernel module is
if [ -e /boot/System.map-#KVERS# ]; then
depmod -a -F /boot/System.map-#KVERS# #KVERS# || true
fi
so with no System.map installed the snippet does nothing at all.
Today that is already wrong. make modules_install writes modules.dep before
bundle-dkms-modules.sh adds anything under /lib/modules/<kver>/extra/, so the
modules.dep we ship has never listed the bundled DKMS modules, and nothing on
the target regenerates it. modprobe kgsl cannot work on an installed image.
Install System.map-<kver> alongside the other boot artifacts, and treat a
missing one as an error rather than copying it if it happens to be there: it
is produced by every kernel build that produced the vmlinuz next to it, so its
absence means something is wrong with the build tree, not with the kernel's
configuration.
This is also what Debian and Ubuntu kernels ship, and what crash,
makedumpfile and perf look for.
Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The bundler writes into two staging trees named on the command line, --image-pkg-dir and --dbg-pkg-dir. Once each module ships in its own binary package that stops being enough: the set of trees to write into is derived from the module list, not known when the script is invoked, so the caller can only name the directory the trees are created in. Add --stage-root for that directory -- debian/ in a source package -- and require it. Nothing reads it yet; this commit only establishes the option, its validation and its plumbing from debian/rules, so the commit that moves the staging is about the move alone. Required rather than optional, and with no default derived from the other paths: every caller already passes four staging arguments, a fifth is no extra burden, and guessing debian/ from the parent of --image-pkg-dir would be wrong for the standalone developer path the script documents. Validated the way --headers-dir is: absolute, because the script is documented as callable from anywhere, and existing, because it is a directory the caller staged rather than one we create. Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
Every module named in DKMS_MODULES was copied into linux-image-<kver> under /lib/modules/<kver>/extra/, so a system installing the kernel got kgsl, camx, iris-vpu and audioreach whether or not it had the hardware for any of them, and the only way to decline one was a rootfs-wide /etc/modprobe.d blacklist. That blacklist is not tied to any kernel package, so it survives kernel upgrades and kernel switches and goes on suppressing the modules for kernels that wanted them. Ship each module in its own <name>-modules-<kver> package instead, with its debug symbols in a matching -dbg. The modules are still built from the same -dkms source against the kernel produced by the same build, and a listed module is still a presence contract; only where they land changes. 'prepare' appends a pair of stanzas per entry in DKMS_LIST, substituted from the new debian/control-dkms.in. They cannot be spelled in control.in because there is one per element of a list that is a build input, and they come from the same DKMS_LIST that drives Build-Depends and the dkms-modules manifest, so a module cannot be declared in control without being built or vice versa. Conflicts and Replaces, both unversioned, against the -dkms package. The two forms are deliberately mutually exclusive: a -dkms install builds a second copy into updates/dkms/, which sits at the same depmod precedence as ours, so allowing both would leave which module loads up to depmod's ordering. Replaces costs nothing and makes the overlap declaratively legal rather than incidentally avoided. The modules move from extra/ to updates/qli/. extra/ is not in depmod's default search order on Debian at all -- it is Fedora's convention, and dkms picks it only there -- so the bundled modules have had the *lowest* precedence rather than the highest. updates/qli/ is the exact structural sibling of the updates/dkms/ a real DKMS install would use. Three consequences handled here: - The in-tree collision guard matters more, not less. Under extra/ an in-tree module of the same name won; under updates/ ours silently wins. The guard stays and its message now says which way the precedence runs. - The duplicate-basename guard had to change shape. It tested [[ -e "$dest" ]] against the one shared image tree; with a tree per package that test sees only the current module, while on the target they all unpack into a single updates/qli/ and still collide. It is now a run-scoped associative array keyed on basename, naming the manifest entry that claimed it. - A generated stanza with nothing staged under it produces a silently empty .deb -- dh_installdeb, dh_gencontrol and dh_builddeb are all happy with an empty tree and dh_missing does not look here. Assert a non-zero staged count per module package, so a wrong --stage-root fails where it happened. --dbg-pkg-dir is dropped: the debug trees are derived from --stage-root now. --image-pkg-dir stays, read-only, for the in-tree module list and the boot/config-<kver> that BUILD_EXCLUSIVE_CONFIG analysis reads. Only the modules are shipped. Whatever modprobe.d snippets, udev rules or initramfs hooks a -dkms package carries are not carried over with them; that is a separate change, and the package description says so rather than leaving it to be discovered. Verified by running prepare over a stub kernel tree: DKMS_MODULES=kgsl,camx yields 9 binary packages, all parsing clean through Dpkg::Control::Info; an empty list yields the same 5 packages as before with an unchanged Build-Depends. Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The .ko in <name>-modules-<kver> is compiled from source that lives in a
different source package entirely, and nothing in the Debian metadata says so.
The binary is emitted by src:linux-qcom-next, which does not contain a line of
the module's code, so an archive holding the binary has no reason to keep the
source that produced it.
Built-Using names that source and its exact version, which is what makes the
archive retain it alongside the binary. The bundler is the only place that can
know the value: it is a property of the -dkms package installed in the build
chroot, not of anything in this source tree, so it cannot be spelled in the
template and reaches the stanza as a substvar.
${source:Package} / ${source:Version} rather than the binary's own name and
version. dpkg parses these out of the binary's "Source: name (ver)" field when
it has one and falls back to the binary version otherwise, epoch included.
Writing the file during override_dh_auto_install is safe: dh_prep is the only
thing that truncates a .substvars and it runs before dh_auto_install, while
every writer after that point merges through addsubstvar. The write filters
any previous key out rather than appending, because this script is documented
as callable by hand outside the dh sequence, where nothing has cleared the
file and a second run would otherwise leave two copies of the key.
A failed dpkg-query is fatal with our own message rather than dpkg's. The
package resolved through dpkg -L a few lines earlier, so a failure here means
something genuinely strange rather than a missing build dependency.
The generated files are added to .gitignore and debian/clean alongside the
other build-time artifacts, so a dirty tree does not carry them.
Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The modules a variant builds are only installable by their versioned names,
<name>-modules-<KVER>. That name changes on every snapshot, so nothing can
depend on "the kgsl modules for this kernel" the way linux-image-qcom-next
already lets something depend on "the newest image for this variant": an
image metapackage exists, a module one does not, and a user who wants the
modules to follow the kernel has to reinstall by hand after each build.
Append a third stanza per DKMS_LIST entry, <BINPKG>-modules-<name>, depending
on that entry's versioned package at (= ${binary:Version}). It is generated in
the same loop, from the same debian/control-dkms.in, so a module cannot gain a
metapackage without being built or be built without gaining one.
Named off BINPKG rather than from a stem derived out of it. The variant
already has exactly one unversioned name the matrix gives it, and extending it
means every unversioned package a variant publishes shares one prefix --
linux-image-qcom-next, linux-image-qcom-next-modules-kgsl -- so they list
together and no second naming scheme has to be kept in step with the first.
The substitution is @binpkg@, which control.in already spells, so the loop
only had to pass it through.
No -dbg metapackage. Debug symbols are fetched for a specific build, which is
the case the versioned name already serves.
Verified by running prepare over a stub kernel tree: DKMS_MODULES=kgsl,camx
now yields 11 binary packages, parsing clean through Dpkg::Control::Info, and
an empty list still yields the same 5 with an unchanged Build-Depends.
Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
A variant now has one metapackage per module, but nothing names the set. An
image or a rootfs recipe that wants everything this kernel was built with has
to enumerate the modules itself, which means the list lives in two places --
the dkms field in ci/build-matrix.json and whatever consumes the packages --
and drifts the first time one of them gains an entry.
Append one <BINPKG>-modules stanza, generated from the new
debian/control-dkms-all.in, depending on every <BINPKG>-modules-<name> the
loop above just emitted. Installing that one name pulls in the whole
out-of-tree module set for the variant, and the set is described where it is
decided rather than by whoever installs it.
It depends on the metapackages, not on the versioned packages, so what it
names survives a snapshot: only the metapackages underneath it have to
re-point at new versioned names, and this stanza's Depends list changes only
when DKMS_MODULES itself does.
Emitted only when DKMS_LIST is non-empty. A variant that bundles nothing would
otherwise publish an empty metapackage depending on nothing, which is not a
thing anyone would install on purpose. @DKMS_META_DEPENDS@ is appended after
${misc:Depends} the same way @DKMS_BUILD_DEPENDS@ is appended to
Build-Depends, so the field never carries a dangling comma.
Verified by running prepare over a stub kernel tree: DKMS_MODULES=kgsl,camx
now yields 12 binary packages, parsing clean through Dpkg::Control::Info, with
linux-image-qcom-next-modules depending on both per-module metapackages;
BINPKG=linux-image-qcom-next-debug names them all off that prefix instead; and
an empty list still yields the same 5 packages with an unchanged Build-Depends.
Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The docs still described the previous arrangement throughout: modules bundled into linux-image-<KVER>, five binary packages per variant, DKMS debug symbols under the image's -dbg in extra/. All of that changed. debian/README.md carries the substance. The "DKMS module bundling" section becomes "Out-of-tree DKMS modules" and gains what the arrangement now is: the packages each list entry produces, why they Conflict with the -dkms package they came from, what Built-Using records and why the module source is never copied into src:linux-qcom-next. Two things there are worth writing down because they are not visible in any one file. Why updates/qli/ rather than extra/: extra/ is not in depmod's default search order on Debian at all, so the modules had the lowest precedence rather than the highest. And why /boot/System.map-<KVER> matters to these packages: the depmod snippet debhelper generates for each of them is guarded by its presence. The section also states plainly that only the modules are shipped -- the modprobe.d snippets, udev rules and initramfs hooks a -dkms package carries are not carried over with them -- so a reader does not have to infer it from the absence of any mention. The remaining edits are corrections rather than additions: the package naming table and count, the -dbg package's installed paths, the matrix's dkms field description and artifact table in the top-level README, and the --dkms help in prepare-source.sh and build-kernel.sh. Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
Christopher Obbard (obbardc)
had a problem deploying
to
Staging
September 18, 2026 00:23 — with
GitHub Actions
Failure
Christopher Obbard (obbardc)
deployed
to
Staging
September 18, 2026 00:23 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 18, 2026 00:23 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 18, 2026 00:24 — with
GitHub Actions
Active
Christopher Obbard (obbardc)
deployed
to
Staging
September 18, 2026 01:20 — with
GitHub Actions
Active
13 tasks
Contributor
Author
|
Initial testing so far so good on trixie && forky: base kernel package - OK
kgsl - OK
camx - OKiris-vpu - OKshipped files: before reboot: after reboot: audioreach - OKafter reboot: Remaining
|
Christopher Obbard (obbardc)
marked this pull request as ready for review
September 18, 2026 07:30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every module named in
DKMS_MODULESis currently built atdpkg-buildpackagetime and copied into
linux-image-<KVER>under/lib/modules/<KVER>/extra/. Asystem that installs the kernel gets
kgsl,camx,iris-vpuandaudioreachwhether or not it has the hardware for any of them, and the rootfsloads them when matching hardware appears.
The only way to decline one is a global
/etc/modprobe.dblacklist in therootfs. That file is not tied to any kernel package, so it survives kernel
upgrades and kernel switches and goes on suppressing the out-of-tree modules
for kernels that wanted them. The unit of choice should be a package, not a
rootfs-wide config file.
Each module now ships in its own
<name>-modules-<KVER>package with amatching
-dbg, an unversioned<BINPKG>-modules-<name>metapackage trackingthe newest build of the variant, and one
<BINPKG>-modulescovering the wholeset.
DKMS_MODULES=kgsl,camxyields 12 binary packages where it used to yield5; an empty list still yields the same 5 with an unchanged
Build-Depends.The modules also move from
extra/toupdates/qli/.extra/is a Fedoraconvention and is not in depmod's default search order on Debian at all, so the
bundled modules have had the lowest precedence rather than the highest.
updates/qli/is the structural sibling of theupdates/dkms/a real DKMSinstall would use, which is also why the new packages
Conflicts/Replacesthe corresponding
-dkmspackage: two copies at equal precedence would leavewhich one loads up to depmod's ordering.
System.mapcomes first in the series and is a prerequisite, not a drive-by:the depmod snippet debhelper generates for every package shipping a module is
guarded by
[ -e /boot/System.map-<KVER> ], and the image did not ship one, sonothing on the target ever regenerated
modules.dep. That is already wrongtoday —
make modules_installwritesmodules.depbefore the bundler addsanything under
extra/— and it would leave the new packages unloadable.Not in this PR, deliberately
.kofiles and nothing else.