Lunatik

Running scripts

The lunatik command

usage: lunatik [-h | -V]
       lunatik [-i] [-e <chunk>]
       lunatik load | unload | reload | status
       lunatik run [-c process | softirq | hardirq] [-p] <script>
       lunatik spawn <script>
       lunatik stop <script>...
       lunatik list
       lunatik test [<suite>]
       lunatik compile [<lunatic argument>...]

An operation the kernel refuses exits 1 with lunatik: <message> on stderr and nothing on stdout, and a wrong invocation exits 2 with the usage on stderr.

The CLI reaches the kernel through /dev/lunatik, whose protocol is internal to it: a tool drives Lunatik through the lunatik command, not through the device. A driver whose reply carries no status, as the one a release before 5.0 loads, fails every command that reaches it with lunatik: couldn't read /dev/lunatik: loaded from another build; unload that release with its own CLI before installing another over it, or reboot.

Execution contexts

A runtime is created in one of three contexts, and this decides what its code may do:

Context How Allocation Lock May sleep
process default, and always for spawn GFP_KERNEL mutex yes
softirq lunatik run --context=softirq <script> GFP_ATOMIC spin_lock_bh; spin_lock_irqsave with IRQs already off no
hardirq lunatik run --context=hardirq <script> GFP_ATOMIC spin_lock_irqsave, always no

A binding that registers a hook checks that the runtime registering it has the context the hook fires in, and raises runtime context mismatch: <class> needs <context> from a runtime of another context. A class whose objects may sleep is a process-context class, and creating one of its objects from a softirq or hardirq runtime raises '<class>': process-context class in interrupt-context runtime. A method of a shared object whose lock is a spinlock, a softirq or hardirq one, runs under that lock, and the runtime that calls it allocates with GFP_ATOMIC until it returns, a process runtime included.

The script body itself runs once, in process context, before the runtime is armed, so registering hooks at its top level may sleep. Everything that runs later, from a hook, may not. A runtime is armed once its script body returns; from then on, in a softirq or hardirq runtime, a call that sleeps, such as creating or stopping a kprobe, raises not allowed once the runtime is armed.

Kernel threads

A script for spawn returns the thread body. The body polls thread.shouldstop() and yields, and bounds every call that blocks with a timeout: lunatik stop waits for the body to return, and a body that never returns keeps it waiting. An error the body raises goes to the kernel log.

local thread = require("thread")
local linux  = require("linux")

return function()
    while not thread.shouldstop() do
        -- non blocking work only
     linux.schedule(100)
    end
end

lunatic

usage: lunatic [options] [filenames]

lunatic is luac built with the host compiler from the same lua/ sources and configuration as lunatik.ko, so its chunks match the kernel’s opcode set and integer-only number format; chunks from the distribution luac are rejected by the kernel. The options are luac’s (-l list, -o output, -p parse only, -s strip debug information, -v version) plus -e big|little, the byte order of the target when it is not the host’s, and lunatik compile runs it with the same arguments.

A chunk is installed and run under the usual .lua name; the kernel detects it by its signature. Several inputs make one chunk that runs them in order, as with luac, so compile one file per call. -s drops the source name and the line numbers, so a stripped chunk reports an error as ?:?: ...; keep the full chunk while developing:

lunatik compile -o hello.luac hello.lua
sudo install -m 0644 hello.luac /lib/modules/lua/hello.lua
sudo lunatik run hello

sudo make install BYTECODE=1, after make, installs the kernel Lua libraries and the examples as stripped chunks instead of source, so an error raised from one of them reads ?:?:.

Edit this page on GitHub