Load the drivers for a machine’s hardware
A liken machine loads only the drivers its manifest names. Nothing
loads a driver on demand. This guide identifies the hardware a machine
cannot drive, names the drivers for it in load order, sets the
parameters they need, and confirms the device appears. At the end,
the hardware operator that owns the device publishes it.
You need:
- A running machine, with
kubectlaccess to itsMachineresource. - The machine’s manifest in your deployment directory, so the next install stick agrees with the cluster.
Why nothing loads on its own
A Linux distribution loads most drivers on demand: the kernel detects a
device, asks userspace for the driver that matches it, and modprobe
or udev loads it. liken ships neither. The manifest is the whole
truth about what a machine runs, and the boot loads
spec.modules
in the
order the list gives. A driver the list does not name never loads,
and that includes the drivers a subsystem would have pulled in on
its own: a sound codec’s parser, a PHY library, a cipher.
The hardware report on the install stick names the drivers for disks and network ports, in load order. It loads nothing else on purpose, so every other device needs this guide once.
1. Read what the machine cannot drive
The machine reports every device the kernel found and no driver controls, with the modules whose alias patterns match it:
kubectl get machine <name> -o jsonpath='{.status.hardware.unclaimed}' | jq
Each entry names the device, its candidate modules in the kernel
build’s order of preference, and the correction. More than one
candidate is usual: USB storage matches both uas and usb_storage,
and the choice is yours. A device that is absent from this list and
absent from every operator’s devices has a driver that bound it to
the wrong thing, which the next section covers.
2. Work out the driver set
A candidate module is the controller’s driver. A controller often needs sub-drivers loaded before it, or it binds the device to a generic driver and the device never appears the way the operator expects. The cases that come up:
- Sound. A codec’s own driver must load before the controller’s.
With
snd_hda_intelalone, the controller binds the codec to the generic parser, and the outputs never appear. Name the codec parser, then the vendor codec driver, then the controller. - Network. A driver can name a soft dependency that must load
before it, such as a PHY library. Without it the port binds to a
generic PHY and the link does not come up the same way. A module
records its soft dependencies in its own
.modinfosection, andmodinfo -F softdep <module>on a workstation with the same kernel family prints them. - Bluetooth. A machine whose adapter serves a remote or a
keyboard needs
uhidbeside the adapter’s driver, and Give a workload a device says why. - USB-CEC adapters. A Pulse-Eight or RainShadow adapter needs
three modules:
cdc_acm, which creates its serial line,serport, and the adapter’s own driver,pulse8_cecorrainshadow_cec. The adapter’s driver binds only after the machine attaches the serial line, which aspec.serioentry declares. See Attach a USB-CEC adapter .
Write the set down with the sub-drivers first and the controller last.
3. Declare the modules
Add the modules to the machine’s manifest, in that order, and apply it:
spec:
modules:
- snd_hda_codec_hdmi
- snd_hda_codec_intelhdmi
- snd_hda_intel
Or patch the live Machine and copy the result into the manifest
afterwards:
kubectl patch machine <name> --type=merge \
-p '{"spec":{"modules":["snd_hda_codec_hdmi","snd_hda_codec_intelhdmi","snd_hda_intel"]}}'
A merge patch replaces the whole list, so send the full list every time. An addition loads live, without a reboot. Removing a module needs a reboot, and a change in the order alone stages for the next boot with no reboot request, because the machine asks for nothing it can apply live.
Attach a USB-CEC adapter
A USB-CEC adapter’s driver is a serio driver. It binds to a serio
port, and the kernel creates that port only while a program holds
the serport line discipline on the adapter’s serial line. On a
general-purpose distribution, udev starts
inputattach
to hold it, as the kernel’s
CEC admin guide
describes. On liken, the machine holds the attachment for the life
of the boot. A
spec.serio
entry names the
protocol and the adapter’s USB identity, and the modules go in
spec.modules as usual:
spec:
modules:
- cdc_acm
- serport
- pulse8_cec
serio:
- protocol: pulse8-cec
usb:
vendor: "2548"
product: "1002"
The entry matches by the adapter’s vendor and product, not by the
tty name, because the kernel numbers ttyACM0 and ttyACM1 in the
order the adapters were plugged in. Add usb.serial to match one
unit when a machine has two adapters of one model. An entry with a
serial takes its adapter first, and an entry without one attaches
the adapters of that model that no entry with a serial names. An
added entry attaches without a reboot, and a removed entry stays
attached until the next boot.
An entry withholds the adapter’s tty from every workload, because
the machine holds the line. A program that drives the adapter itself
over the tty, such as one built on libCEC, needs no entry and no
serport or pulse8_cec: declare cdc_acm alone, and claim the
tty. status.hardware.unclaimed lists an adapter that cdc_acm
drives and no entry attaches, and its message names both uses.
A release older than spec.serio cannot read a manifest that
declares it. Remove the field before you roll a machine back to such
a release, and let the removal stage for the next boot.
Roll back
gives the steps.
4. Set the parameters a driver needs
A load-time setting goes in
spec.moduleParameters
,
keyed <module>.<parameter> in the kernel’s own spelling:
kubectl patch machine <name> --type=merge \
-p '{"spec":{"moduleParameters":{"snd_hda_intel.power_save":"0"}}}'
A parameter applies when its module loads and never after. Declare
the parameter with the module, in one edit, and it applies at the
live load. A parameter added to a module the machine already loaded
applies at the next reboot, and the ModuleParametersApplied
condition says so. A parameter cannot reach a module that is built
into the kernel, and the same condition reports that with the fix.
5. Reboot when a driver bound the wrong device
Whichever driver is registered when a controller probes keeps the device until the machine reboots. So a device that bound to the generic driver before you added its vendor driver stays bound after the live load. Nothing in the spec asks for a reboot in that case, because the machine agrees with every document it was given. Ask for one:
./liken request-reboot mycluster <name>
The machine waits for the cluster to grant its turn and drains
before it goes down, under the same policy as any other reboot.
liken request-reboot
describes the annotation it writes.
6. Confirm the device appears
Read the result of every declared module:
kubectl get machine <name> -o jsonpath='{.status.modules}' | jq
Loaded and Builtin are the good states. Missing means this
kernel has no module by that name, which is usually a misspelling,
because the image includes the kernel’s whole module tree. Failed
means the kernel refused a module it has, and the message names the
correction. status.modules[].parameters shows what the kernel
reports the value for each declared parameter, in the kernel’s own rendering.
Read the result of every spec.serio entry:
kubectl get machine <name> -o jsonpath='{.status.serio}' | jq
Attached is the good state, and nodes lists the devices the
adapter’s driver created, such as /dev/cec0 and the remote’s event
node. Missing means no serial line matches the entry: the adapter
is unplugged, or cdc_acm is not loaded. Refused means a line
matches and the attachment did not complete, and the message names
the module to declare, gives the kernel’s error, or says that the
adapter’s driver did not bind the port. The kernel log then names the
cause. A refused attachment is tried again after a backoff that
grows from one second to five minutes, and at once when the adapter
is plugged in again. The
SerioAttached condition names the first entry that is not
attached. It does not change the machine’s Ready condition,
because a machine with an unplugged adapter still works.
Then read the operator that owns the device. A screen appears in the display operator’s slice, an output in the audio operator’s, a radio in the Bluetooth operator’s:
kubectl get resourceslices
The entry in status.hardware.unclaimed is gone once a driver binds
the device.
7. Write the list into the manifest
A live patch changes the cluster and nothing else. Copy the final
spec.modules, spec.moduleParameters, and spec.serio into the
machine’s manifest in your deployment directory, so the next install
stick and the next reinstall start from the same list. On a fleet run from
git, the commit is the edit, and the machine converges to it.