Skip to content

Commit e85f86f

Browse files
lnetoclaude
andcommitted
lunatik: replace boolean sleep with context string in runtime()
runtime(script, "softirq") for interrupt context (GFP_ATOMIC, spinlock); runtime(script) or runtime(script, "process") for process context (GFP_KERNEL, mutex). Mirrors data.new(size, "single"/"shared") style. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 55590c2 commit e85f86f

8 files changed

Lines changed: 25 additions & 50 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ usage: lunatik [load|unload|reload|status|test|list] [run|spawn|stop <script>]
9595
* `status`: show which Lunatik kernel modules are currently loaded
9696
* `test [suite]`: run installed test suites (see [Testing](#testing))
9797
* `list`: show which runtime environments are currently running
98-
* `run`: create a new runtime environment to run the script `/lib/modules/lua/<script>.lua`
98+
* `run [softirq]`: create a new runtime environment to run the script `/lib/modules/lua/<script>.lua`; pass `softirq` for hooks that fire in atomic context (netfilter, XDP)
9999
* `spawn`: create a new runtime environment and spawn a thread to run the script `/lib/modules/lua/<script>.lua`
100100
* `stop`: stop the runtime environment created to run the script `<script>`
101101
* `default`: start a _REPL (Read–Eval–Print Loop)_
@@ -320,7 +320,7 @@ sudo make btf_install # needed to export the 'bpf_luaxdp_
320320
sudo make examples_install # installs examples
321321
make ebpf # builds the XDP/eBPF program
322322
sudo make ebpf_install # installs the XDP/eBPF program
323-
sudo lunatik run examples/filter/sni false # runs the Lua kernel script
323+
sudo lunatik run examples/filter/sni softirq # runs the Lua kernel script
324324
sudo xdp-loader load -m skb <ifname> https.o # loads the XDP/eBPF program
325325
```
326326

@@ -353,7 +353,7 @@ This script drops any outbound DNS packet with question matching the blacklist p
353353

354354
```
355355
sudo make examples_install # installs examples
356-
sudo lunatik run examples/dnsblock/nf_dnsblock false # runs the Lua kernel script
356+
sudo lunatik run examples/dnsblock/nf_dnsblock softirq # runs the Lua kernel script
357357
```
358358

359359
### dnsdoctor
@@ -372,7 +372,7 @@ examples/dnsdoctor/setup.sh # sets up the environment
372372
dig lunatik.com
373373
374374
# run the Lua kernel script
375-
sudo lunatik run examples/dnsdoctor/nf_dnsdoctor false
375+
sudo lunatik run examples/dnsdoctor/nf_dnsdoctor softirq
376376
377377
# test the setup, a response with IP 10.1.2.3 should be returned
378378
dig lunatik.com
@@ -434,7 +434,7 @@ It supports gestures: swiping right locks the mouse, and swiping left unlocks it
434434

435435
```
436436
sudo make examples_install # installs examples
437-
sudo lunatik run examples/gesture false # runs gesture
437+
sudo lunatik run examples/gesture softirq # runs gesture
438438
# In QEMU window:
439439
# Drag right to lock the mouse
440440
# Drag left to unlock the mouse
@@ -450,7 +450,7 @@ It fixes the report descriptor for the device (`0x2717`:`0x5014`).
450450

451451
```
452452
sudo make examples_install # installs examples
453-
sudo lunatik run examples/xiaomi false # runs xiaomi driver
453+
sudo lunatik run examples/xiaomi softirq # runs xiaomi driver
454454
```
455455

456456
Then insert the Xiaomi Silent Mouse with bluetooth mode on and it should work properly.

bin/lunatik

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ if #arg >= 1 then
157157
os.exit(false)
158158
end
159159
ensure_loaded()
160-
local parm = arg[3] and ("," .. arg[3]) or ""
160+
local parm = arg[3] and (', "' .. arg[3] .. '"') or ""
161161
local ret = token == "list" and "return" or ""
162162
local chunk = string.format("%s lunatik.runner.%s('%s'%s)", ret, token, script, parm)
163163
print(dostring(chunk))

examples/tcpreject/setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ nft add rule ip6 tcpreject forward \
5353
ip6 daddr $DNS6 tcp dport 443 mark set $MARK
5454

5555
# load the Lua hook
56-
lunatik run examples/tcpreject/nf_tcpreject false
56+
lunatik run examples/tcpreject/nf_tcpreject softirq
5757

5858
echo "setup done"
5959
echo "test IPv4: ip netns exec $NETNS curl --connect-timeout 2 https://$DNS4"

lib/lunatik/runner.lua

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--
2-
-- SPDX-FileCopyrightText: (c) 2023-2024 Ring Zero Desenvolvimento de Software LTDA
2+
-- SPDX-FileCopyrightText: (c) 2023-2026 Ring Zero Desenvolvimento de Software LTDA
33
-- SPDX-License-Identifier: MIT OR GPL-2.0-only
44
--
55

@@ -32,7 +32,7 @@ end
3232
-- Creates a new Lunatik runtime for the given script and registers it.
3333
-- Throws an error if a script with the same name is already running.
3434
-- @tparam string script The path or name of the Lua script to run. The ".lua" extension will be trimmed.
35-
-- @param ... Additional arguments to pass to the script's main function.
35+
-- @tparam[opt] string context Execution context: `"process"` (default) or `"softirq"` (for netfilter/XDP hooks).
3636
-- @treturn table The created Lunatik runtime object.
3737
-- @raise error if the script is already running.
3838
function runner.run(script, ...)
@@ -50,7 +50,6 @@ end
5050
-- to execute the runtime. The thread is named based on the script's filename.
5151
-- The spawned script is expected to return a function, which will then be executed in the new thread.
5252
-- @tparam string script The path or name of the Lua script to spawn.
53-
-- @param ... Additional arguments to pass to the script's main function.
5453
-- @treturn userdata The kernel thread object.
5554
function runner.spawn(script, ...)
5655
local runtime = runner.run(script, ...)
@@ -91,8 +90,6 @@ function runner.list()
9190
rcu.map(env.runtimes, function (script)
9291
table.insert(list, script)
9392
end)
94-
-- The original code `table.concat(list, ', ')` correctly returns an empty string
95-
-- if `list` is empty, so no change to the code logic is needed or made here.
9693
return table.concat(list, ', ')
9794
end
9895

lunatik_core.c

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,7 @@ static inline void lunatik_setversion(lua_State *L)
5151
(((f) == GFP_ATOMIC || (n) <= PAGE_SIZE) && (!is_vmalloc_addr(p) || (p) == NULL))
5252

5353
/***
54-
* Represents a Lunatik runtime environment.
55-
* This is a userdata object returned by `lunatik.runtime()`. It encapsulates an
56-
* isolated Lua state running within the Linux kernel. Each runtime can be
57-
* configured as sleepable (allowing blocking operations, using `GFP_KERNEL`
58-
* for allocations and mutexes for synchronization) or non-sleepable/atomic
59-
* (prohibiting blocking operations, using `GFP_ATOMIC` for allocations and
60-
* spinlocks for synchronization).
54+
* Isolated Lua state running within the Linux kernel.
6155
* @type runtime
6256
*/
6357
static void *lunatik_alloc(void *ud, void *optr, size_t osize, size_t nsize)
@@ -306,33 +300,22 @@ int lunatik_runtime(lunatik_object_t **pruntime, const char *script, lunatik_opt
306300
EXPORT_SYMBOL(lunatik_runtime);
307301

308302
/***
309-
* Creates and starts a new Lunatik runtime environment.
310-
* A Lunatik runtime is an isolated Lua state that can execute Lua scripts
311-
* within the kernel. Each runtime operates independently.
312-
*
313-
* The userdata object returned by `lunatik.runtime()` encapsulates an
314-
* isolated Lua state running within the Linux kernel. Each runtime can be
315-
* configured as sleepable (allowing blocking operations, using `GFP_KERNEL`
316-
* for allocations and mutexes for synchronization) or non-sleepable/atomic
317-
* (prohibiting blocking operations, using `GFP_ATOMIC` for allocations and
318-
* spinlocks for synchronization).
319-
303+
* Creates a new Lunatik runtime executing the given script.
320304
* @function runtime
321-
* @tparam string script The name of the Lua script to load and execute (e.g., "myscript").
322-
* The system will look for "myscript.lua" in the Lua root path.
323-
* @tparam[opt=true] boolean sleep If `true` (default),
324-
* the runtime can sleep (e.g., for I/O operations) and uses `GFP_KERNEL` for allocations.
325-
* If `false`, the runtime operates in an atomic context, cannot sleep, and uses `GFP_ATOMIC` for allocations.
326-
* This is crucial for runtimes used in contexts that cannot sleep, like Netfilter hooks.
327-
* @treturn runtime A Lunatik runtime object. This object can be used to interact with the runtime, for example,
328-
* to resume it if it yields or to stop it.
329-
* @raise Error if the Lua state or runtime cannot be allocated, or if the script fails to load or execute.
305+
* @tparam string script script name (e.g., `"mymod"` loads `/lib/modules/lua/mymod.lua`)
306+
* @tparam[opt="process"] string context execution context: `"process"` (sleepable,
307+
* GFP\_KERNEL, mutex) or `"softirq"` (atomic, GFP\_ATOMIC, spinlock).
308+
* Use `"softirq"` for hooks that fire in interrupt context (netfilter, XDP).
309+
* @treturn runtime
310+
* @raise if allocation fails or the script errors on load
330311
* @within lunatik
331312
*/
332313
static int lunatik_lruntime(lua_State *L)
333314
{
315+
static const char *const contexts[] = {"process", "softirq", NULL};
334316
const char *script = luaL_checkstring(L, 1);
335-
lunatik_opt_t opt = (lua_gettop(L) >= 2 && !lua_toboolean(L, 2)) ? LUNATIK_OPT_SOFTIRQ : LUNATIK_OPT_NONE;
317+
int context = luaL_checkoption(L, 2, "process", contexts);
318+
lunatik_opt_t opt = context == 1 ? LUNATIK_OPT_SOFTIRQ : LUNATIK_OPT_NONE;
336319

337320
lunatik_object_t **pruntime = lunatik_newpobject(L, 1);
338321
if (lunatik_newruntime(pruntime, L, script, opt) != 0)

tests/runtime/opt_guards.sh

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,7 @@ ktap_plan 1
2222

2323
mark_dmesg
2424

25-
output=$(lunatik run "$SCRIPT")
26-
if echo "$output" | grep -qE "\.lua:[0-9]+:"; then
27-
ktap_fail "Lua error in script"
28-
echo "# $output"
29-
ktap_totals
30-
exit 1
31-
fi
25+
run_script "$SCRIPT"
3226

3327
check_dmesg || { ktap_totals; exit 1; }
3428
ktap_pass "opt guards: SINGLE rejected, MONITOR/NONE accepted"

tests/runtime/opt_skb_single.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ cat /sys/module/$MODULE/refcnt > /dev/null 2>&1 || {
3030

3131
mark_dmesg
3232

33-
lunatik run "$SCRIPT" false
33+
run_script "$SCRIPT" softirq
34+
3435
ping -c 1 -W 1 127.0.0.1 > /dev/null 2>&1 || true
3536

3637
check_dmesg || { ktap_totals; exit 1; }

tests/runtime/refcnt_leak.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mark_dmesg
3333

3434
# run the script in a non-sleepable runtime (required for netfilter hooks);
3535
# it is expected to fail — ignore the error
36-
lunatik run "$SCRIPT" false 2>/dev/null || true
36+
lunatik run "$SCRIPT" softirq 2>/dev/null || true
3737

3838
check_dmesg || { ktap_totals; exit 1; }
3939

0 commit comments

Comments
 (0)