Skip to content

Commit ecd2509

Browse files
committed
livepatch: Selftests of the API for tracking system state changes
Four selftests for the new API. Link: http://lkml.kernel.org/r/[email protected] To: Jiri Kosina <[email protected]> Cc: Kamalesh Babulal <[email protected]> Cc: Nicolai Stange <[email protected]> Cc: [email protected] Cc: [email protected] Acked-by: Miroslav Benes <[email protected]> Acked-by: Joe Lawrence <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Signed-off-by: Petr Mladek <[email protected]>
1 parent e553d2a commit ecd2509

File tree

6 files changed

+544
-2
lines changed

6 files changed

+544
-2
lines changed

lib/livepatch/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
88
test_klp_callbacks_busy.o \
99
test_klp_callbacks_mod.o \
1010
test_klp_livepatch.o \
11-
test_klp_shadow_vars.o
11+
test_klp_shadow_vars.o \
12+
test_klp_state.o \
13+
test_klp_state2.o \
14+
test_klp_state3.o
1215

1316
# Target modules to be livepatched require CC_FLAGS_FTRACE
1417
CFLAGS_test_klp_callbacks_busy.o += $(CC_FLAGS_FTRACE)

lib/livepatch/test_klp_state.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (C) 2019 SUSE
3+
4+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5+
6+
#include <linux/slab.h>
7+
#include <linux/module.h>
8+
#include <linux/kernel.h>
9+
#include <linux/printk.h>
10+
#include <linux/livepatch.h>
11+
12+
#define CONSOLE_LOGLEVEL_STATE 1
13+
/* Version 1 does not support migration. */
14+
#define CONSOLE_LOGLEVEL_STATE_VERSION 1
15+
16+
static const char *const module_state[] = {
17+
[MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
18+
[MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",
19+
[MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",
20+
[MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
21+
};
22+
23+
static void callback_info(const char *callback, struct klp_object *obj)
24+
{
25+
if (obj->mod)
26+
pr_info("%s: %s -> %s\n", callback, obj->mod->name,
27+
module_state[obj->mod->state]);
28+
else
29+
pr_info("%s: vmlinux\n", callback);
30+
}
31+
32+
static struct klp_patch patch;
33+
34+
static int allocate_loglevel_state(void)
35+
{
36+
struct klp_state *loglevel_state;
37+
38+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
39+
if (!loglevel_state)
40+
return -EINVAL;
41+
42+
loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);
43+
if (!loglevel_state->data)
44+
return -ENOMEM;
45+
46+
pr_info("%s: allocating space to store console_loglevel\n",
47+
__func__);
48+
return 0;
49+
}
50+
51+
static void fix_console_loglevel(void)
52+
{
53+
struct klp_state *loglevel_state;
54+
55+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
56+
if (!loglevel_state)
57+
return;
58+
59+
pr_info("%s: fixing console_loglevel\n", __func__);
60+
*(int *)loglevel_state->data = console_loglevel;
61+
console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
62+
}
63+
64+
static void restore_console_loglevel(void)
65+
{
66+
struct klp_state *loglevel_state;
67+
68+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
69+
if (!loglevel_state)
70+
return;
71+
72+
pr_info("%s: restoring console_loglevel\n", __func__);
73+
console_loglevel = *(int *)loglevel_state->data;
74+
}
75+
76+
static void free_loglevel_state(void)
77+
{
78+
struct klp_state *loglevel_state;
79+
80+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
81+
if (!loglevel_state)
82+
return;
83+
84+
pr_info("%s: freeing space for the stored console_loglevel\n",
85+
__func__);
86+
kfree(loglevel_state->data);
87+
}
88+
89+
/* Executed on object patching (ie, patch enablement) */
90+
static int pre_patch_callback(struct klp_object *obj)
91+
{
92+
callback_info(__func__, obj);
93+
return allocate_loglevel_state();
94+
}
95+
96+
/* Executed on object unpatching (ie, patch disablement) */
97+
static void post_patch_callback(struct klp_object *obj)
98+
{
99+
callback_info(__func__, obj);
100+
fix_console_loglevel();
101+
}
102+
103+
/* Executed on object unpatching (ie, patch disablement) */
104+
static void pre_unpatch_callback(struct klp_object *obj)
105+
{
106+
callback_info(__func__, obj);
107+
restore_console_loglevel();
108+
}
109+
110+
/* Executed on object unpatching (ie, patch disablement) */
111+
static void post_unpatch_callback(struct klp_object *obj)
112+
{
113+
callback_info(__func__, obj);
114+
free_loglevel_state();
115+
}
116+
117+
static struct klp_func no_funcs[] = {
118+
{}
119+
};
120+
121+
static struct klp_object objs[] = {
122+
{
123+
.name = NULL, /* vmlinux */
124+
.funcs = no_funcs,
125+
.callbacks = {
126+
.pre_patch = pre_patch_callback,
127+
.post_patch = post_patch_callback,
128+
.pre_unpatch = pre_unpatch_callback,
129+
.post_unpatch = post_unpatch_callback,
130+
},
131+
}, { }
132+
};
133+
134+
static struct klp_state states[] = {
135+
{
136+
.id = CONSOLE_LOGLEVEL_STATE,
137+
.version = CONSOLE_LOGLEVEL_STATE_VERSION,
138+
}, { }
139+
};
140+
141+
static struct klp_patch patch = {
142+
.mod = THIS_MODULE,
143+
.objs = objs,
144+
.states = states,
145+
.replace = true,
146+
};
147+
148+
static int test_klp_callbacks_demo_init(void)
149+
{
150+
return klp_enable_patch(&patch);
151+
}
152+
153+
static void test_klp_callbacks_demo_exit(void)
154+
{
155+
}
156+
157+
module_init(test_klp_callbacks_demo_init);
158+
module_exit(test_klp_callbacks_demo_exit);
159+
MODULE_LICENSE("GPL");
160+
MODULE_INFO(livepatch, "Y");
161+
MODULE_AUTHOR("Petr Mladek <[email protected]>");
162+
MODULE_DESCRIPTION("Livepatch test: system state modification");

lib/livepatch/test_klp_state2.c

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (C) 2019 SUSE
3+
4+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5+
6+
#include <linux/slab.h>
7+
#include <linux/module.h>
8+
#include <linux/kernel.h>
9+
#include <linux/printk.h>
10+
#include <linux/livepatch.h>
11+
12+
#define CONSOLE_LOGLEVEL_STATE 1
13+
/* Version 2 supports migration. */
14+
#define CONSOLE_LOGLEVEL_STATE_VERSION 2
15+
16+
static const char *const module_state[] = {
17+
[MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
18+
[MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",
19+
[MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",
20+
[MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
21+
};
22+
23+
static void callback_info(const char *callback, struct klp_object *obj)
24+
{
25+
if (obj->mod)
26+
pr_info("%s: %s -> %s\n", callback, obj->mod->name,
27+
module_state[obj->mod->state]);
28+
else
29+
pr_info("%s: vmlinux\n", callback);
30+
}
31+
32+
static struct klp_patch patch;
33+
34+
static int allocate_loglevel_state(void)
35+
{
36+
struct klp_state *loglevel_state, *prev_loglevel_state;
37+
38+
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
39+
if (prev_loglevel_state) {
40+
pr_info("%s: space to store console_loglevel already allocated\n",
41+
__func__);
42+
return 0;
43+
}
44+
45+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
46+
if (!loglevel_state)
47+
return -EINVAL;
48+
49+
loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);
50+
if (!loglevel_state->data)
51+
return -ENOMEM;
52+
53+
pr_info("%s: allocating space to store console_loglevel\n",
54+
__func__);
55+
return 0;
56+
}
57+
58+
static void fix_console_loglevel(void)
59+
{
60+
struct klp_state *loglevel_state, *prev_loglevel_state;
61+
62+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
63+
if (!loglevel_state)
64+
return;
65+
66+
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
67+
if (prev_loglevel_state) {
68+
pr_info("%s: taking over the console_loglevel change\n",
69+
__func__);
70+
loglevel_state->data = prev_loglevel_state->data;
71+
return;
72+
}
73+
74+
pr_info("%s: fixing console_loglevel\n", __func__);
75+
*(int *)loglevel_state->data = console_loglevel;
76+
console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
77+
}
78+
79+
static void restore_console_loglevel(void)
80+
{
81+
struct klp_state *loglevel_state, *prev_loglevel_state;
82+
83+
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
84+
if (prev_loglevel_state) {
85+
pr_info("%s: passing the console_loglevel change back to the old livepatch\n",
86+
__func__);
87+
return;
88+
}
89+
90+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
91+
if (!loglevel_state)
92+
return;
93+
94+
pr_info("%s: restoring console_loglevel\n", __func__);
95+
console_loglevel = *(int *)loglevel_state->data;
96+
}
97+
98+
static void free_loglevel_state(void)
99+
{
100+
struct klp_state *loglevel_state, *prev_loglevel_state;
101+
102+
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
103+
if (prev_loglevel_state) {
104+
pr_info("%s: keeping space to store console_loglevel\n",
105+
__func__);
106+
return;
107+
}
108+
109+
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
110+
if (!loglevel_state)
111+
return;
112+
113+
pr_info("%s: freeing space for the stored console_loglevel\n",
114+
__func__);
115+
kfree(loglevel_state->data);
116+
}
117+
118+
/* Executed on object patching (ie, patch enablement) */
119+
static int pre_patch_callback(struct klp_object *obj)
120+
{
121+
callback_info(__func__, obj);
122+
return allocate_loglevel_state();
123+
}
124+
125+
/* Executed on object unpatching (ie, patch disablement) */
126+
static void post_patch_callback(struct klp_object *obj)
127+
{
128+
callback_info(__func__, obj);
129+
fix_console_loglevel();
130+
}
131+
132+
/* Executed on object unpatching (ie, patch disablement) */
133+
static void pre_unpatch_callback(struct klp_object *obj)
134+
{
135+
callback_info(__func__, obj);
136+
restore_console_loglevel();
137+
}
138+
139+
/* Executed on object unpatching (ie, patch disablement) */
140+
static void post_unpatch_callback(struct klp_object *obj)
141+
{
142+
callback_info(__func__, obj);
143+
free_loglevel_state();
144+
}
145+
146+
static struct klp_func no_funcs[] = {
147+
{}
148+
};
149+
150+
static struct klp_object objs[] = {
151+
{
152+
.name = NULL, /* vmlinux */
153+
.funcs = no_funcs,
154+
.callbacks = {
155+
.pre_patch = pre_patch_callback,
156+
.post_patch = post_patch_callback,
157+
.pre_unpatch = pre_unpatch_callback,
158+
.post_unpatch = post_unpatch_callback,
159+
},
160+
}, { }
161+
};
162+
163+
static struct klp_state states[] = {
164+
{
165+
.id = CONSOLE_LOGLEVEL_STATE,
166+
.version = CONSOLE_LOGLEVEL_STATE_VERSION,
167+
}, { }
168+
};
169+
170+
static struct klp_patch patch = {
171+
.mod = THIS_MODULE,
172+
.objs = objs,
173+
.states = states,
174+
.replace = true,
175+
};
176+
177+
static int test_klp_callbacks_demo_init(void)
178+
{
179+
return klp_enable_patch(&patch);
180+
}
181+
182+
static void test_klp_callbacks_demo_exit(void)
183+
{
184+
}
185+
186+
module_init(test_klp_callbacks_demo_init);
187+
module_exit(test_klp_callbacks_demo_exit);
188+
MODULE_LICENSE("GPL");
189+
MODULE_INFO(livepatch, "Y");
190+
MODULE_AUTHOR("Petr Mladek <[email protected]>");
191+
MODULE_DESCRIPTION("Livepatch test: system state modification");

lib/livepatch/test_klp_state3.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (C) 2019 SUSE
3+
4+
/* The console loglevel fix is the same in the next cumulative patch. */
5+
#include "test_klp_state2.c"

tools/testing/selftests/livepatch/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ TEST_PROGS_EXTENDED := functions.sh
44
TEST_PROGS := \
55
test-livepatch.sh \
66
test-callbacks.sh \
7-
test-shadow-vars.sh
7+
test-shadow-vars.sh \
8+
test-state.sh
89

910
include ../lib.mk

0 commit comments

Comments
 (0)