Skip to content

Commit dfb2a15

Browse files
Caio Luizlneto
Caio Luiz
authored andcommitted
import LuaRCU implementation from GSoC 2018
luainkernel/lunatik#6
0 parents  commit dfb2a15

File tree

5 files changed

+891
-0
lines changed

5 files changed

+891
-0
lines changed

luarcu.c

+339
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
/*
2+
* Copyright (c) 2018 Caio Luiz <[email protected]>.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining
5+
* a copy of this software and associated documentation files (the
6+
* "Software"), to deal in the Software without restriction, including
7+
* without limitation the rights to use, copy, modify, merge, publish,
8+
* distribute, sublicense, and/or sell copies of the Software, and to
9+
* permit persons to whom the Software is furnished to do so, subject to
10+
* the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be
13+
* included in all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22+
*/
23+
#include <linux/module.h>
24+
#include <linux/kernel.h>
25+
#include <linux/slab.h>
26+
#include <linux/list.h>
27+
#include <linux/rculist.h>
28+
#include <linux/spinlock.h>
29+
#include <linux/preempt.h>
30+
#include <linux/hashtable.h>
31+
32+
#include <lua/lua.h>
33+
#include <lua/lauxlib.h>
34+
#include <lua/lualib.h>
35+
#include <lua/llimits.h>
36+
37+
struct tvalue {
38+
union {
39+
int i;
40+
bool b;
41+
const char *s;
42+
};
43+
int type;
44+
};
45+
46+
struct element {
47+
char *key;
48+
struct tvalue value;
49+
struct hlist_node node;
50+
struct rcu_head rcu;
51+
};
52+
53+
#define BITS 3
54+
#define NBUCKETS (1 << BITS)
55+
#define MASK (NBUCKETS -1)
56+
static DEFINE_HASHTABLE(rcutable, BITS);
57+
static spinlock_t bucket_lock[NBUCKETS];
58+
static bool first_time_setup = true;
59+
60+
#if !defined(LUAI_HASHLIMIT)
61+
#define LUAI_HASHLIMIT 5
62+
#endif
63+
64+
static unsigned int lua_hash_str(const char *str, size_t l, unsigned int seed) {
65+
unsigned int h = seed ^ cast(unsigned int, l);
66+
size_t step = (l >> LUAI_HASHLIMIT) + 1;
67+
for (; l >= step; l -= step)
68+
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
69+
return h;
70+
}
71+
72+
#define hash_str(str) \
73+
lua_hash_str(str, strlen(str), 0)
74+
75+
static struct element* rcu_search_element(const char *key, int idx) {
76+
struct element *e;
77+
78+
hlist_for_each_entry(e, &rcutable[idx], node) {
79+
if (strcmp(e->key, key) == 0) {
80+
return e;
81+
}
82+
}
83+
84+
return NULL;
85+
}
86+
87+
static int rcu_add_element(lua_State *L, const char *key, struct tvalue value, int idx) {
88+
struct element *e;
89+
90+
e = kmalloc(sizeof(struct element), GFP_ATOMIC);
91+
if (!e) luaL_error(L, "could not allocate memory");
92+
93+
e->key = kmalloc(strlen(key) +1, GFP_ATOMIC); //+1 also consider a \0
94+
if (!e->key) luaL_error(L, "could not allocate memory");
95+
strcpy(e->key, key);
96+
97+
e->value.type = value.type;
98+
switch (e->value.type) {
99+
case LUA_TSTRING:
100+
e->value.s = kmalloc(strlen(value.s) +1, GFP_ATOMIC);
101+
if (!e->value.s) luaL_error(L, "could not allocate memory");
102+
strcpy(e->value.s, value.s);
103+
break;
104+
case LUA_TNUMBER:
105+
e->value.i = value.i;
106+
break;
107+
case LUA_TBOOLEAN:
108+
e->value.b = value.b;
109+
break;
110+
default:
111+
printk("could not add the element: unsupported type");
112+
return 0;
113+
}
114+
115+
hlist_add_head_rcu(&e->node, &rcutable[idx]);
116+
117+
if (value.type == LUA_TSTRING)
118+
printk("added pair %s - %s to bucket %d", key, value.s, idx);
119+
else {
120+
if (value.type == LUA_TNUMBER)
121+
printk("added pair %s - %d to bucket %d", key, value.i, idx);
122+
else printk("added pair %s - %d to bucket %d", key, value.b, idx);
123+
}
124+
125+
return 0;
126+
}
127+
128+
129+
static int rcu_each(lua_State *L) {
130+
int bkt = 0;
131+
struct element *e = NULL;
132+
133+
/* Whenever we call a function with lua_pcall,
134+
* both the arguments and the function are removed from the stack.
135+
* Since we want to call the function for each element, we must copy
136+
* the function on each iteration.
137+
* Also, lua_copy actually replaces an element on the stack, so
138+
* we push a nil to guarantee a valid index.
139+
*/
140+
141+
rcu_read_lock();
142+
hash_for_each_rcu(rcutable, bkt, e, node) {
143+
lua_pushnil(L);
144+
lua_copy(L, 1, 2);
145+
if (e->value.type == LUA_TSTRING)
146+
lua_pushstring(L, e->value.s);
147+
if (e->value.type == LUA_TNUMBER)
148+
lua_pushinteger(L, e->value.i);
149+
if (e->value.type == LUA_TBOOLEAN)
150+
lua_pushboolean(L, e->value.b);
151+
lua_pcall(L, 1, 0, 0);
152+
}
153+
rcu_read_unlock();
154+
155+
return 0;
156+
}
157+
158+
static int rcu_delete_element(struct element *e, int idx) {
159+
hlist_del_rcu(&e->node);
160+
printk("deleted key %s on bucket %d", e->key, idx);
161+
162+
spin_unlock(&bucket_lock[idx]);
163+
synchronize_rcu();
164+
165+
if (e->value.type == LUA_TSTRING)
166+
kfree(e->value.s);
167+
168+
kfree(e->key);
169+
kfree(e);
170+
171+
return 0;
172+
}
173+
174+
static int rcu_replace_element(lua_State *L, struct element *e,
175+
struct tvalue new_value, int idx) {
176+
struct element *new_e = NULL;
177+
178+
new_e = kmalloc(sizeof(struct element), GFP_ATOMIC);
179+
if (!new_e) luaL_error(L, "could not allocate memory");
180+
181+
new_e->key = kmalloc(strlen(e->key) +1, GFP_ATOMIC);
182+
if (!new_e->key) luaL_error(L, "could not allocate memory");
183+
strcpy(new_e->key, e->key);
184+
185+
switch (new_value.type) {
186+
case LUA_TSTRING:
187+
new_e->value.s = kmalloc(strlen(new_value.s) +1, GFP_ATOMIC);
188+
if (!new_e->value.s) luaL_error(L, "could not allocate memory");
189+
strcpy(new_e->value.s, new_value.s);
190+
new_e->value.type = LUA_TSTRING;
191+
break;
192+
case LUA_TNUMBER:
193+
new_e->value.i = new_value.i;
194+
new_e->value.type = LUA_TNUMBER;
195+
break;
196+
case LUA_TBOOLEAN:
197+
new_e->value.b = new_value.b;
198+
new_e->value.type = LUA_TBOOLEAN;
199+
break;
200+
default:
201+
printk("could not replace the element: unsupported type");
202+
return 0;
203+
}
204+
205+
hlist_replace_rcu(&e->node, &new_e->node);
206+
207+
spin_unlock(&bucket_lock[idx]);
208+
synchronize_rcu();
209+
210+
if (e->value.type == LUA_TSTRING)
211+
kfree(e->value.s);
212+
213+
kfree(e->key);
214+
kfree(e);
215+
216+
if (new_value.type == LUA_TSTRING)
217+
printk("updated key %s to value %s on bucket %d", new_e->key, new_value.s, idx);
218+
else {
219+
if (new_value.type == LUA_TNUMBER)
220+
printk("updated key %s to value %d on bucket %d", new_e->key, new_value.i, idx);
221+
else printk("updated key %s to value %d on bucket %d", new_e->key, new_value.b, idx);
222+
}
223+
224+
return 0;
225+
}
226+
227+
static int rcu_index(lua_State *L) {
228+
struct element *e;
229+
const char *key;
230+
int idx;
231+
232+
luaL_checktype(L, -1, LUA_TSTRING);
233+
key = lua_tostring(L, -1);
234+
idx = hash_str(key) & MASK;
235+
236+
rcu_read_lock();
237+
e = rcu_search_element(key, idx);
238+
if (e != NULL) {
239+
if (e->value.type == LUA_TSTRING)
240+
lua_pushstring(L, e->value.s);
241+
if (e->value.type == LUA_TNUMBER)
242+
lua_pushinteger(L, e->value.i);
243+
if (e->value.type == LUA_TBOOLEAN)
244+
lua_pushboolean(L, e->value.b);
245+
}
246+
else lua_pushnil(L);
247+
rcu_read_unlock();
248+
249+
return 1;
250+
}
251+
252+
253+
static int rcu_newindex(lua_State *L) {
254+
struct element *e;
255+
const char *key;
256+
struct tvalue in;
257+
int idx;
258+
259+
/* Only strings are allowed for keys.
260+
* Value allows for strings, bools and ints or nil (meaning removal).
261+
* Raises an error if the user supply a different type.
262+
*/
263+
264+
luaL_checktype(L, -2, LUA_TSTRING);
265+
266+
if (lua_type(L, -1) > LUA_TSTRING)
267+
luaL_argerror(L, -1, "expected a string, int, bool or nil for value");
268+
269+
key = lua_tostring(L, -2);
270+
idx = hash_str(key) & MASK;
271+
272+
in.type = lua_type(L, -1);
273+
switch (in.type) {
274+
case LUA_TSTRING:
275+
in.s = lua_tostring(L, -1);
276+
break;
277+
case LUA_TNUMBER:
278+
in.i = lua_tointeger(L, -1);
279+
break;
280+
case LUA_TBOOLEAN:
281+
in.b = lua_toboolean(L, -1);
282+
break;
283+
case LUA_TNIL:
284+
break;
285+
default:
286+
printk("couldn't complete the operation: unsupported type");
287+
return 0;
288+
}
289+
290+
spin_lock(&bucket_lock[idx]);
291+
e = rcu_search_element(key, idx);
292+
if (e != NULL && in.type == LUA_TNIL) {
293+
rcu_delete_element(e, idx);
294+
return 0;
295+
}
296+
297+
if (e == NULL && in.type != LUA_TNIL) {
298+
rcu_add_element(L, key, in, idx);
299+
spin_unlock(&bucket_lock[idx]);
300+
return 0;
301+
}
302+
303+
if (e != NULL && in.type != LUA_TNIL) {
304+
rcu_replace_element(L, e, in, idx);
305+
return 0;
306+
}
307+
308+
spin_unlock(&bucket_lock[idx]);
309+
return 0;
310+
}
311+
312+
static const struct luaL_Reg rcu_funcs[] = {
313+
{"for_each", rcu_each},
314+
{NULL, NULL}
315+
};
316+
317+
static const struct luaL_Reg rcu_methods[] = {
318+
{"__newindex", rcu_newindex},
319+
{"__index", rcu_index},
320+
{NULL, NULL}
321+
};
322+
323+
int luaopen_rcu(lua_State *L) {
324+
int i;
325+
326+
if (first_time_setup) {
327+
hash_init(rcutable);
328+
for (i = 0; i < NBUCKETS; i++)
329+
spin_lock_init(&bucket_lock[i]);
330+
first_time_setup = false;
331+
}
332+
333+
luaL_newmetatable(L, "Rcu.hash");
334+
luaL_setfuncs(L, rcu_methods, 0);
335+
luaL_newlib(L, rcu_funcs);
336+
luaL_setmetatable(L, "Rcu.hash");
337+
338+
return 1;
339+
}

0 commit comments

Comments
 (0)