Commit f9d13814 authored by Jiri Kosina's avatar Jiri Kosina

Merge branch 'for-5.1/atomic-replace' into for-linus

The atomic replace allows to create cumulative patches. They are useful when
you maintain many livepatches and want to remove one that is lower on the
stack. In addition it is very useful when more patches touch the same function
and there are dependencies between them.

It's also a feature some of the distros are using already to distribute
their patches.
parents 7185a969 fbb76d57
...@@ -522,7 +522,7 @@ static int klp_add_nops(struct klp_patch *patch) ...@@ -522,7 +522,7 @@ static int klp_add_nops(struct klp_patch *patch)
struct klp_patch *old_patch; struct klp_patch *old_patch;
struct klp_object *old_obj; struct klp_object *old_obj;
list_for_each_entry(old_patch, &klp_patches, list) { klp_for_each_patch(old_patch) {
klp_for_each_object(old_patch, old_obj) { klp_for_each_object(old_patch, old_obj) {
int err; int err;
...@@ -1004,7 +1004,7 @@ int klp_enable_patch(struct klp_patch *patch) ...@@ -1004,7 +1004,7 @@ int klp_enable_patch(struct klp_patch *patch)
if (!klp_have_reliable_stack()) { if (!klp_have_reliable_stack()) {
pr_err("This architecture doesn't have support for the livepatch consistency model.\n"); pr_err("This architecture doesn't have support for the livepatch consistency model.\n");
return -ENOSYS; return -EOPNOTSUPP;
} }
...@@ -1057,7 +1057,7 @@ void klp_discard_replaced_patches(struct klp_patch *new_patch) ...@@ -1057,7 +1057,7 @@ void klp_discard_replaced_patches(struct klp_patch *new_patch)
{ {
struct klp_patch *old_patch, *tmp_patch; struct klp_patch *old_patch, *tmp_patch;
list_for_each_entry_safe(old_patch, tmp_patch, &klp_patches, list) { klp_for_each_patch_safe(old_patch, tmp_patch) {
if (old_patch == new_patch) if (old_patch == new_patch)
return; return;
...@@ -1101,7 +1101,7 @@ static void klp_cleanup_module_patches_limited(struct module *mod, ...@@ -1101,7 +1101,7 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
struct klp_patch *patch; struct klp_patch *patch;
struct klp_object *obj; struct klp_object *obj;
list_for_each_entry(patch, &klp_patches, list) { klp_for_each_patch(patch) {
if (patch == limit) if (patch == limit)
break; break;
...@@ -1109,12 +1109,6 @@ static void klp_cleanup_module_patches_limited(struct module *mod, ...@@ -1109,12 +1109,6 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
if (!klp_is_module(obj) || strcmp(obj->name, mod->name)) if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
continue; continue;
/*
* Only unpatch the module if the patch is enabled or
* is in transition.
*/
if (patch->enabled || patch == klp_transition_patch) {
if (patch != klp_transition_patch) if (patch != klp_transition_patch)
klp_pre_unpatch_callback(obj); klp_pre_unpatch_callback(obj);
...@@ -1123,7 +1117,6 @@ static void klp_cleanup_module_patches_limited(struct module *mod, ...@@ -1123,7 +1117,6 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
klp_unpatch_object(obj); klp_unpatch_object(obj);
klp_post_unpatch_callback(obj); klp_post_unpatch_callback(obj);
}
klp_free_object_loaded(obj); klp_free_object_loaded(obj);
break; break;
...@@ -1148,7 +1141,7 @@ int klp_module_coming(struct module *mod) ...@@ -1148,7 +1141,7 @@ int klp_module_coming(struct module *mod)
*/ */
mod->klp_alive = true; mod->klp_alive = true;
list_for_each_entry(patch, &klp_patches, list) { klp_for_each_patch(patch) {
klp_for_each_object(patch, obj) { klp_for_each_object(patch, obj) {
if (!klp_is_module(obj) || strcmp(obj->name, mod->name)) if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
continue; continue;
...@@ -1162,13 +1155,6 @@ int klp_module_coming(struct module *mod) ...@@ -1162,13 +1155,6 @@ int klp_module_coming(struct module *mod)
goto err; goto err;
} }
/*
* Only patch the module if the patch is enabled or is
* in transition.
*/
if (!patch->enabled && patch != klp_transition_patch)
break;
pr_notice("applying patch '%s' to loading module '%s'\n", pr_notice("applying patch '%s' to loading module '%s'\n",
patch->mod->name, obj->mod->name); patch->mod->name, obj->mod->name);
......
...@@ -7,6 +7,12 @@ ...@@ -7,6 +7,12 @@
extern struct mutex klp_mutex; extern struct mutex klp_mutex;
extern struct list_head klp_patches; extern struct list_head klp_patches;
#define klp_for_each_patch_safe(patch, tmp_patch) \
list_for_each_entry_safe(patch, tmp_patch, &klp_patches, list)
#define klp_for_each_patch(patch) \
list_for_each_entry(patch, &klp_patches, list)
void klp_free_patch_start(struct klp_patch *patch); void klp_free_patch_start(struct klp_patch *patch);
void klp_discard_replaced_patches(struct klp_patch *new_patch); void klp_discard_replaced_patches(struct klp_patch *new_patch);
void klp_discard_nops(struct klp_patch *new_patch); void klp_discard_nops(struct klp_patch *new_patch);
......
...@@ -652,6 +652,6 @@ void klp_force_transition(void) ...@@ -652,6 +652,6 @@ void klp_force_transition(void)
for_each_possible_cpu(cpu) for_each_possible_cpu(cpu)
klp_update_patch_state(idle_task(cpu)); klp_update_patch_state(idle_task(cpu));
list_for_each_entry(patch, &klp_patches, list) klp_for_each_patch(patch)
patch->forced = true; patch->forced = true;
} }
...@@ -44,7 +44,7 @@ static int ptr_id(void *ptr) ...@@ -44,7 +44,7 @@ static int ptr_id(void *ptr)
sp = kmalloc(sizeof(*sp), GFP_ATOMIC); sp = kmalloc(sizeof(*sp), GFP_ATOMIC);
if (!sp) if (!sp)
return -1; return -ENOMEM;
sp->ptr = ptr; sp->ptr = ptr;
sp->id = count++; sp->id = count++;
...@@ -154,22 +154,37 @@ static int test_klp_shadow_vars_init(void) ...@@ -154,22 +154,37 @@ static int test_klp_shadow_vars_init(void)
* Allocate a few shadow variables with different <obj> and <id>. * Allocate a few shadow variables with different <obj> and <id>.
*/ */
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1); sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
if (!sv1)
return -ENOMEM;
sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2); sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
if (!sv2)
return -ENOMEM;
sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3); sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
if (!sv3)
return -ENOMEM;
/* /*
* Verify we can find our new shadow variables and that they point * Verify we can find our new shadow variables and that they point
* to expected data. * to expected data.
*/ */
ret = shadow_get(obj, id); ret = shadow_get(obj, id);
if (!ret)
return -EINVAL;
if (ret == sv1 && *sv1 == &var1) if (ret == sv1 && *sv1 == &var1)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv1), ptr_id(*sv1)); ptr_id(sv1), ptr_id(*sv1));
ret = shadow_get(obj + 1, id); ret = shadow_get(obj + 1, id);
if (!ret)
return -EINVAL;
if (ret == sv2 && *sv2 == &var2) if (ret == sv2 && *sv2 == &var2)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv2), ptr_id(*sv2)); ptr_id(sv2), ptr_id(*sv2));
ret = shadow_get(obj, id + 1); ret = shadow_get(obj, id + 1);
if (!ret)
return -EINVAL;
if (ret == sv3 && *sv3 == &var3) if (ret == sv3 && *sv3 == &var3)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3)); ptr_id(sv3), ptr_id(*sv3));
...@@ -179,7 +194,12 @@ static int test_klp_shadow_vars_init(void) ...@@ -179,7 +194,12 @@ static int test_klp_shadow_vars_init(void)
* The second invocation should return the same shadow var. * The second invocation should return the same shadow var.
*/ */
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
if (!sv4)
return -ENOMEM;
ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
if (!ret)
return -EINVAL;
if (ret == sv4 && *sv4 == &var4) if (ret == sv4 && *sv4 == &var4)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv4), ptr_id(*sv4)); ptr_id(sv4), ptr_id(*sv4));
...@@ -207,6 +227,8 @@ static int test_klp_shadow_vars_init(void) ...@@ -207,6 +227,8 @@ static int test_klp_shadow_vars_init(void)
* We should still find an <id+1> variable. * We should still find an <id+1> variable.
*/ */
ret = shadow_get(obj, id + 1); ret = shadow_get(obj, id + 1);
if (!ret)
return -EINVAL;
if (ret == sv3 && *sv3 == &var3) if (ret == sv3 && *sv3 == &var3)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3)); ptr_id(sv3), ptr_id(*sv3));
......
...@@ -55,11 +55,10 @@ function is_livepatch_mod() { ...@@ -55,11 +55,10 @@ function is_livepatch_mod() {
function __load_mod() { function __load_mod() {
local mod="$1"; shift local mod="$1"; shift
local args="$*"
local msg="% modprobe $mod $args" local msg="% modprobe $mod $*"
log "${msg%% }" log "${msg%% }"
ret=$(modprobe "$mod" "$args" 2>&1) ret=$(modprobe "$mod" "$@" 2>&1)
if [[ "$ret" != "" ]]; then if [[ "$ret" != "" ]]; then
die "$ret" die "$ret"
fi fi
...@@ -75,12 +74,11 @@ function __load_mod() { ...@@ -75,12 +74,11 @@ function __load_mod() {
# params - module parameters to pass to modprobe # params - module parameters to pass to modprobe
function load_mod() { function load_mod() {
local mod="$1"; shift local mod="$1"; shift
local args="$*"
is_livepatch_mod "$mod" && is_livepatch_mod "$mod" &&
die "use load_lp() to load the livepatch module $mod" die "use load_lp() to load the livepatch module $mod"
__load_mod "$mod" "$args" __load_mod "$mod" "$@"
} }
# load_lp_nowait(modname, params) - load a kernel module with a livepatch # load_lp_nowait(modname, params) - load a kernel module with a livepatch
...@@ -89,12 +87,11 @@ function load_mod() { ...@@ -89,12 +87,11 @@ function load_mod() {
# params - module parameters to pass to modprobe # params - module parameters to pass to modprobe
function load_lp_nowait() { function load_lp_nowait() {
local mod="$1"; shift local mod="$1"; shift
local args="$*"
is_livepatch_mod "$mod" || is_livepatch_mod "$mod" ||
die "module $mod is not a livepatch" die "module $mod is not a livepatch"
__load_mod "$mod" "$args" __load_mod "$mod" "$@"
# Wait for livepatch in sysfs ... # Wait for livepatch in sysfs ...
loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' || loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' ||
...@@ -106,9 +103,8 @@ function load_lp_nowait() { ...@@ -106,9 +103,8 @@ function load_lp_nowait() {
# params - module parameters to pass to modprobe # params - module parameters to pass to modprobe
function load_lp() { function load_lp() {
local mod="$1"; shift local mod="$1"; shift
local args="$*"
load_lp_nowait "$mod" "$args" load_lp_nowait "$mod" "$@"
# Wait until the transition finishes ... # Wait until the transition finishes ...
loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' || loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' ||
...@@ -120,11 +116,10 @@ function load_lp() { ...@@ -120,11 +116,10 @@ function load_lp() {
# params - module parameters to pass to modprobe # params - module parameters to pass to modprobe
function load_failing_mod() { function load_failing_mod() {
local mod="$1"; shift local mod="$1"; shift
local args="$*"
local msg="% modprobe $mod $args" local msg="% modprobe $mod $*"
log "${msg%% }" log "${msg%% }"
ret=$(modprobe "$mod" "$args" 2>&1) ret=$(modprobe "$mod" "$@" 2>&1)
if [[ "$ret" == "" ]]; then if [[ "$ret" == "" ]]; then
die "$mod unexpectedly loaded" die "$mod unexpectedly loaded"
fi fi
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment