From 190d80428705b67b34bb5a709464fdf0e1a2081c Mon Sep 17 00:00:00 2001 From: Savio Sena Date: Sun, 8 Sep 2024 07:19:29 -0300 Subject: [PATCH] add luacompletion --- Kbuild | 1 + Makefile | 2 +- README.md | 24 +++++++++++ bin/lunatik | 2 +- lib/luacompletion.c | 100 ++++++++++++++++++++++++++++++++++++++++++++ lib/mailbox.lua | 25 +++++++---- 6 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 lib/luacompletion.c diff --git a/Kbuild b/Kbuild index 53f4d28c..b13b9e7e 100644 --- a/Kbuild +++ b/Kbuild @@ -43,4 +43,5 @@ obj-$(CONFIG_LUNATIK_XDP) += lib/luaxdp.o obj-$(CONFIG_LUNATIK_FIFO) += lib/luafifo.o obj-$(CONFIG_LUNATIK_XTABLE) += lib/luaxtable.o obj-$(CONFIG_LUNATIK_NETFILTER) += lib/luanetfilter.o +obj-$(CONFIG_LUNATIK_COMPLETION) += lib/luacompletion.o diff --git a/Makefile b/Makefile index 2e79dcea..ec172541 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ all: lunatik_sym.h CONFIG_LUNATIK_RCU=m CONFIG_LUNATIK_THREAD=m CONFIG_LUNATIK_FIB=m \ CONFIG_LUNATIK_DATA=m CONFIG_LUNATIK_PROBE=m CONFIG_LUNATIK_SYSCALL=m \ CONFIG_LUNATIK_XDP=m CONFIG_LUNATIK_FIFO=m CONFIG_LUNATIK_XTABLE=m \ - CONFIG_LUNATIK_NETFILTER=m + CONFIG_LUNATIK_NETFILTER=m CONFIG_LUNATIK_COMPLETION=m clean: ${MAKE} -C ${KDIR} M=${PWD} clean diff --git a/README.md b/README.md index a9aece48..c13eafa1 100644 --- a/README.md +++ b/README.md @@ -1348,6 +1348,30 @@ address families to Lua. * `"NETDEV"`: Device ingress and egress path * `"BRIDGE"`: Ethernet Bridge. +### `completion` + +The `completion` library provides support for the [kernel completion primitives](https://docs.kernel.org/scheduler/completion.html). + +Task completion is a synchronization mechanism used to coordinate the execution of multiple threads, similar to `pthread_barrier`, it allows threads to wait for a specific event to occur before proceeding, ensuring certain tasks are complete in a race-free manner. + +#### `completion.new()` + +_completion.new()_ creates a new `completion` object. + +#### `c:complete()` + +_c:complete()_ signals a single thread waiting on this completion. + +#### `c:wait([timeout])` + +_c:wait()_ waits for completion of a task until the specified timeout expires. +The timeout is specified in milliseconds. If the `timeout` parameter is omitted, it waits indefinitely. Passing a timeout value less than zero results in undefined behavior. +Threads waiting for events can be interrupted by signals, for example, such as when `thread.stop` is invoked. +Therefore, this function can return in three ways: +* If it succeeds, it returns `true` +* If the timeout is reached, it returns `nil, "timeout"` +* If the task is interrupted, it returns `nil, "interrupt"` + # Examples ### spyglass diff --git a/bin/lunatik b/bin/lunatik index 98c0ccad..a64e9e4d 100755 --- a/bin/lunatik +++ b/bin/lunatik @@ -9,7 +9,7 @@ local lunatik = { device = "/dev/lunatik", modules = {"lunatik", "luadevice", "lualinux", "luanotifier", "luasocket", "luarcu", "luathread", "luafib", "luadata", "luaprobe", "luasyscall", "luaxdp", "luafifo", "luaxtable", - "luanetfilter", "lunatik_run"} + "luanetfilter", "luacompletion", "lunatik_run"} } function lunatik.prompt() diff --git a/lib/luacompletion.c b/lib/luacompletion.c new file mode 100644 index 00000000..0ae609d1 --- /dev/null +++ b/lib/luacompletion.c @@ -0,0 +1,100 @@ +/* +* SPDX-FileCopyrightText: (c) 2023-2024 Ring Zero Desenvolvimento de Software LTDA +* SPDX-License-Identifier: MIT OR GPL-2.0-only +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include + +#include +#include +#include + +#include + +LUNATIK_OBJECTCHECKER(luacompletion_check, struct completion *); + +static int luacompletion_complete(lua_State *L) +{ + struct completion *completion = luacompletion_check(L, 1); + + complete(completion); + return 0; +} + +static int luacompletion_wait(lua_State *L) +{ + struct completion *completion = luacompletion_check(L, 1); + lua_Integer timeout = luaL_optinteger(L, 2, MAX_SCHEDULE_TIMEOUT); + unsigned long timeout_jiffies = msecs_to_jiffies((unsigned long)timeout); + long ret; + + lunatik_checkruntime(L, true); + ret = wait_for_completion_interruptible_timeout(completion, timeout_jiffies); + if (ret > 0) { + lua_pushboolean(L, true); + return 1; + } + + lua_pushnil(L); + switch (ret) { + case 0: + lua_pushliteral(L, "timeout"); + break; + case -ERESTARTSYS: + lua_pushliteral(L, "interrupt"); + break; + default: + lua_pushliteral(L, "unknown"); + break; + } + return 2; +} + +static int luacompletion_new(lua_State *L); + +static const luaL_Reg luacompletion_lib[] = { + {"new", luacompletion_new}, + {NULL, NULL} +}; + +static const luaL_Reg luacompletion_mt[] = { + {"__gc", lunatik_deleteobject}, + {"complete", luacompletion_complete}, + {"wait", luacompletion_wait}, + {NULL, NULL} +}; + +static const lunatik_class_t luacompletion_class = { + .name = "completion", + .methods = luacompletion_mt, + .sleep = false, +}; + +static int luacompletion_new(lua_State *L) +{ + lunatik_object_t *object = lunatik_newobject(L, &luacompletion_class, sizeof(struct completion)); + struct completion *completion = (struct completion *)object->private; + + init_completion(completion); + return 1; +} + +LUNATIK_NEWLIB(completion, luacompletion_lib, &luacompletion_class, NULL); + +static int __init luacompletion_init(void) +{ + return 0; +} + +static void __exit luacompletion_exit(void) +{ +} + +module_init(luacompletion_init); +module_exit(luacompletion_exit); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Savio Sena "); + diff --git a/lib/mailbox.lua b/lib/mailbox.lua index f4ae2ea9..27206aa3 100644 --- a/lib/mailbox.lua +++ b/lib/mailbox.lua @@ -3,29 +3,37 @@ -- SPDX-License-Identifier: MIT OR GPL-2.0-only -- -local fifo = require("fifo") +local fifo = require("fifo") +local completion = require("completion") local mailbox = {} local MailBox = {} MailBox.__index = MailBox -local function new(o, allowed, forbidden) +local function new(q, e, allowed, forbidden) local mbox = {} - mbox.queue = type(o) == 'userdata' and o or fifo.new(o) + if type(q) == 'userdata' then + mbox.queue, mbox.event = q, e + else + mbox.queue, mbox.event = fifo.new(q), completion.new() + end mbox[forbidden] = function () error(allowed .. "-only mailbox") end return setmetatable(mbox, MailBox) end -function mailbox.inbox(o) - return new(o, 'receive', 'send') +function mailbox.inbox(q, e) + return new(q, e, 'receive', 'send') end -function mailbox.outbox(o) - return new(o, 'send', 'receive') +function mailbox.outbox(q, e) + return new(q, e, 'send', 'receive') end local sizeoft = string.packsize("T") function MailBox:receive() + local ok, err = self.event:wait() + if not ok then error(err) end + local queue = self.queue local header, header_size = queue:pop(sizeoft) @@ -39,7 +47,8 @@ function MailBox:receive() end function MailBox:send(message) - return self.queue:push(string.pack("s", message)) + self.queue:push(string.pack("s", message)) + self.event:complete() end return mailbox