Livepatching changes for 5.5
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEESH4wyp42V4tXvYsjUqAMR0iAlPIFAl3bz1gACgkQUqAMR0iA lPI5fw//db5dOqAvBu/fz4k38Mc30okgCjtRh0+vhFXCCUXauQv2IhI19J2IiPpy 4t/CjaUk2QSB06NDNUxt7XsuR0yAF4E0nJHUmkDKkN8UFsi7jAjxJ/92zH3x/LE0 YWtVoWjGduO+QfLVlP22VVYh1pX5kOxXG2WTEiJtnkWYdkqtkEy7Cw2Rlzzrrym6 6kIVi3nEPtn/hOnlF/Ii5SJWh+jJrSf+XwXiuIiBupT49Ujoa4KscmhkiHnAccXb ICJpsxBIdZLxHLe/c0YO3b8r4Hvms124vlIC19Z0l9VEJ++sphDOWRrL3Zq9tFw8 FwIKq8Ex9UFfldOnpD5q/PRhM9Xgfsw8UYYZZpXQeW6z7qzv1iVpM6BQlt3dYQaL pb21UXIfzaTdZIsUgnetypbqWIFNZxovrhORpjqxo6WV4lSaVx4MPE2/Le/G8xPR DT+a6yyzTyM0XbZ0MCVDfuZ+ZRaA1zfKEcsYEu883b7yK4z+TZbT61vnEKqq8+40 EgOZnNjBENZLRQY0RofQsO5zGwcaanVOtBOmYDXtP/fup8/1SRZ25zmmIVhvChJG iQwCDw6IMqnae/FsMb+kBTDCRJXpN028iYGY7UAoiCuvzc0qm0gJXsGdZLqMvjEh nEdKKN2ze+03s6I9AcISKdnbUVphhb/xeDKRBkMgcooWLrfWj5E= =i37E -----END PGP SIGNATURE----- Merge tag 'livepatching-for-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching Pull livepatching updates from Petr Mladek: - New API to track system state changes done be livepatch callbacks. It helps to maintain compatibility between livepatches. - Update Kconfig help text. ORC is another reliable unwinder. - Disable generic selftest timeout. Livepatch selftests have their own per-operation fine-grained timeouts. * tag 'livepatching-for-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching: x86/stacktrace: update kconfig help text for reliable unwinders livepatch: Selftests of the API for tracking system state changes livepatch: Documentation of the new API for tracking system state changes livepatch: Allow to distinguish different version of system state changes livepatch: Basic API to track system state changes livepatch: Keep replaced patches until post_patch callback is called selftests/livepatch: Disable the timeout
This commit is contained in:
commit
f838767555
@ -12,6 +12,7 @@ Kernel Livepatching
|
||||
cumulative-patches
|
||||
module-elf-format
|
||||
shadow-vars
|
||||
system-state
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
167
Documentation/livepatch/system-state.rst
Normal file
167
Documentation/livepatch/system-state.rst
Normal file
@ -0,0 +1,167 @@
|
||||
====================
|
||||
System State Changes
|
||||
====================
|
||||
|
||||
Some users are really reluctant to reboot a system. This brings the need
|
||||
to provide more livepatches and maintain some compatibility between them.
|
||||
|
||||
Maintaining more livepatches is much easier with cumulative livepatches.
|
||||
Each new livepatch completely replaces any older one. It can keep,
|
||||
add, and even remove fixes. And it is typically safe to replace any version
|
||||
of the livepatch with any other one thanks to the atomic replace feature.
|
||||
|
||||
The problems might come with shadow variables and callbacks. They might
|
||||
change the system behavior or state so that it is no longer safe to
|
||||
go back and use an older livepatch or the original kernel code. Also
|
||||
any new livepatch must be able to detect what changes have already been
|
||||
done by the already installed livepatches.
|
||||
|
||||
This is where the livepatch system state tracking gets useful. It
|
||||
allows to:
|
||||
|
||||
- store data needed to manipulate and restore the system state
|
||||
|
||||
- define compatibility between livepatches using a change id
|
||||
and version
|
||||
|
||||
|
||||
1. Livepatch system state API
|
||||
=============================
|
||||
|
||||
The state of the system might get modified either by several livepatch callbacks
|
||||
or by the newly used code. Also it must be possible to find changes done by
|
||||
already installed livepatches.
|
||||
|
||||
Each modified state is described by struct klp_state, see
|
||||
include/linux/livepatch.h.
|
||||
|
||||
Each livepatch defines an array of struct klp_states. They mention
|
||||
all states that the livepatch modifies.
|
||||
|
||||
The livepatch author must define the following two fields for each
|
||||
struct klp_state:
|
||||
|
||||
- *id*
|
||||
|
||||
- Non-zero number used to identify the affected system state.
|
||||
|
||||
- *version*
|
||||
|
||||
- Number describing the variant of the system state change that
|
||||
is supported by the given livepatch.
|
||||
|
||||
The state can be manipulated using two functions:
|
||||
|
||||
- *klp_get_state(patch, id)*
|
||||
|
||||
- Get struct klp_state associated with the given livepatch
|
||||
and state id.
|
||||
|
||||
- *klp_get_prev_state(id)*
|
||||
|
||||
- Get struct klp_state associated with the given feature id and
|
||||
already installed livepatches.
|
||||
|
||||
2. Livepatch compatibility
|
||||
==========================
|
||||
|
||||
The system state version is used to prevent loading incompatible livepatches.
|
||||
The check is done when the livepatch is enabled. The rules are:
|
||||
|
||||
- Any completely new system state modification is allowed.
|
||||
|
||||
- System state modifications with the same or higher version are allowed
|
||||
for already modified system states.
|
||||
|
||||
- Cumulative livepatches must handle all system state modifications from
|
||||
already installed livepatches.
|
||||
|
||||
- Non-cumulative livepatches are allowed to touch already modified
|
||||
system states.
|
||||
|
||||
3. Supported scenarios
|
||||
======================
|
||||
|
||||
Livepatches have their life-cycle and the same is true for the system
|
||||
state changes. Every compatible livepatch has to support the following
|
||||
scenarios:
|
||||
|
||||
- Modify the system state when the livepatch gets enabled and the state
|
||||
has not been already modified by a livepatches that are being
|
||||
replaced.
|
||||
|
||||
- Take over or update the system state modification when is has already
|
||||
been done by a livepatch that is being replaced.
|
||||
|
||||
- Restore the original state when the livepatch is disabled.
|
||||
|
||||
- Restore the previous state when the transition is reverted.
|
||||
It might be the original system state or the state modification
|
||||
done by livepatches that were being replaced.
|
||||
|
||||
- Remove any already made changes when error occurs and the livepatch
|
||||
cannot get enabled.
|
||||
|
||||
4. Expected usage
|
||||
=================
|
||||
|
||||
System states are usually modified by livepatch callbacks. The expected
|
||||
role of each callback is as follows:
|
||||
|
||||
*pre_patch()*
|
||||
|
||||
- Allocate *state->data* when necessary. The allocation might fail
|
||||
and *pre_patch()* is the only callback that could stop loading
|
||||
of the livepatch. The allocation is not needed when the data
|
||||
are already provided by previously installed livepatches.
|
||||
|
||||
- Do any other preparatory action that is needed by
|
||||
the new code even before the transition gets finished.
|
||||
For example, initialize *state->data*.
|
||||
|
||||
The system state itself is typically modified in *post_patch()*
|
||||
when the entire system is able to handle it.
|
||||
|
||||
- Clean up its own mess in case of error. It might be done by a custom
|
||||
code or by calling *post_unpatch()* explicitly.
|
||||
|
||||
*post_patch()*
|
||||
|
||||
- Copy *state->data* from the previous livepatch when they are
|
||||
compatible.
|
||||
|
||||
- Do the actual system state modification. Eventually allow
|
||||
the new code to use it.
|
||||
|
||||
- Make sure that *state->data* has all necessary information.
|
||||
|
||||
- Free *state->data* from replaces livepatches when they are
|
||||
not longer needed.
|
||||
|
||||
*pre_unpatch()*
|
||||
|
||||
- Prevent the code, added by the livepatch, relying on the system
|
||||
state change.
|
||||
|
||||
- Revert the system state modification..
|
||||
|
||||
*post_unpatch()*
|
||||
|
||||
- Distinguish transition reverse and livepatch disabling by
|
||||
checking *klp_get_prev_state()*.
|
||||
|
||||
- In case of transition reverse, restore the previous system
|
||||
state. It might mean doing nothing.
|
||||
|
||||
- Remove any not longer needed setting or data.
|
||||
|
||||
.. note::
|
||||
|
||||
*pre_unpatch()* typically does symmetric operations to *post_patch()*.
|
||||
Except that it is called only when the livepatch is being disabled.
|
||||
Therefore it does not need to care about any previously installed
|
||||
livepatch.
|
||||
|
||||
*post_unpatch()* typically does symmetric operations to *pre_patch()*.
|
||||
It might be called also during the transition reverse. Therefore it
|
||||
has to handle the state of the previously installed livepatches.
|
@ -316,10 +316,6 @@ config UNWINDER_FRAME_POINTER
|
||||
unwinder, but the kernel text size will grow by ~3% and the kernel's
|
||||
overall performance will degrade by roughly 5-10%.
|
||||
|
||||
This option is recommended if you want to use the livepatch
|
||||
consistency model, as this is currently the only way to get a
|
||||
reliable stack trace (CONFIG_HAVE_RELIABLE_STACKTRACE).
|
||||
|
||||
config UNWINDER_GUESS
|
||||
bool "Guess unwinder"
|
||||
depends on EXPERT
|
||||
|
@ -130,10 +130,23 @@ struct klp_object {
|
||||
bool patched;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct klp_state - state of the system modified by the livepatch
|
||||
* @id: system state identifier (non-zero)
|
||||
* @version: version of the change
|
||||
* @data: custom data
|
||||
*/
|
||||
struct klp_state {
|
||||
unsigned long id;
|
||||
unsigned int version;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct klp_patch - patch structure for live patching
|
||||
* @mod: reference to the live patch module
|
||||
* @objs: object entries for kernel objects to be patched
|
||||
* @states: system states that can get modified
|
||||
* @replace: replace all actively used patches
|
||||
* @list: list node for global list of actively used patches
|
||||
* @kobj: kobject for sysfs resources
|
||||
@ -147,6 +160,7 @@ struct klp_patch {
|
||||
/* external */
|
||||
struct module *mod;
|
||||
struct klp_object *objs;
|
||||
struct klp_state *states;
|
||||
bool replace;
|
||||
|
||||
/* internal */
|
||||
@ -217,6 +231,9 @@ void *klp_shadow_get_or_alloc(void *obj, unsigned long id,
|
||||
void klp_shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor);
|
||||
void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor);
|
||||
|
||||
struct klp_state *klp_get_state(struct klp_patch *patch, unsigned long id);
|
||||
struct klp_state *klp_get_prev_state(unsigned long id);
|
||||
|
||||
#else /* !CONFIG_LIVEPATCH */
|
||||
|
||||
static inline int klp_module_coming(struct module *mod) { return 0; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_LIVEPATCH) += livepatch.o
|
||||
|
||||
livepatch-objs := core.o patch.o shadow.o transition.o
|
||||
livepatch-objs := core.o patch.o shadow.o state.o transition.o
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#include "core.h"
|
||||
#include "patch.h"
|
||||
#include "state.h"
|
||||
#include "transition.h"
|
||||
|
||||
/*
|
||||
@ -632,7 +633,7 @@ static void klp_free_objects_dynamic(struct klp_patch *patch)
|
||||
* The operation must be completed by calling klp_free_patch_finish()
|
||||
* outside klp_mutex.
|
||||
*/
|
||||
void klp_free_patch_start(struct klp_patch *patch)
|
||||
static void klp_free_patch_start(struct klp_patch *patch)
|
||||
{
|
||||
if (!list_empty(&patch->list))
|
||||
list_del(&patch->list);
|
||||
@ -677,6 +678,23 @@ static void klp_free_patch_work_fn(struct work_struct *work)
|
||||
klp_free_patch_finish(patch);
|
||||
}
|
||||
|
||||
void klp_free_patch_async(struct klp_patch *patch)
|
||||
{
|
||||
klp_free_patch_start(patch);
|
||||
schedule_work(&patch->free_work);
|
||||
}
|
||||
|
||||
void klp_free_replaced_patches_async(struct klp_patch *new_patch)
|
||||
{
|
||||
struct klp_patch *old_patch, *tmp_patch;
|
||||
|
||||
klp_for_each_patch_safe(old_patch, tmp_patch) {
|
||||
if (old_patch == new_patch)
|
||||
return;
|
||||
klp_free_patch_async(old_patch);
|
||||
}
|
||||
}
|
||||
|
||||
static int klp_init_func(struct klp_object *obj, struct klp_func *func)
|
||||
{
|
||||
if (!func->old_name)
|
||||
@ -992,6 +1010,13 @@ int klp_enable_patch(struct klp_patch *patch)
|
||||
|
||||
mutex_lock(&klp_mutex);
|
||||
|
||||
if (!klp_is_patch_compatible(patch)) {
|
||||
pr_err("Livepatch patch (%s) is not compatible with the already installed livepatches.\n",
|
||||
patch->mod->name);
|
||||
mutex_unlock(&klp_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = klp_init_patch_early(patch);
|
||||
if (ret) {
|
||||
mutex_unlock(&klp_mutex);
|
||||
@ -1022,12 +1047,13 @@ err:
|
||||
EXPORT_SYMBOL_GPL(klp_enable_patch);
|
||||
|
||||
/*
|
||||
* This function removes replaced patches.
|
||||
* This function unpatches objects from the replaced livepatches.
|
||||
*
|
||||
* We could be pretty aggressive here. It is called in the situation where
|
||||
* these structures are no longer accessible. All functions are redirected
|
||||
* by the klp_transition_patch. They use either a new code or they are in
|
||||
* the original code because of the special nop function patches.
|
||||
* these structures are no longer accessed from the ftrace handler.
|
||||
* All functions are redirected by the klp_transition_patch. They
|
||||
* use either a new code or they are in the original code because
|
||||
* of the special nop function patches.
|
||||
*
|
||||
* The only exception is when the transition was forced. In this case,
|
||||
* klp_ftrace_handler() might still see the replaced patch on the stack.
|
||||
@ -1035,18 +1061,16 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
|
||||
* thanks to RCU. We only have to keep the patches on the system. Also
|
||||
* this is handled transparently by patch->module_put.
|
||||
*/
|
||||
void klp_discard_replaced_patches(struct klp_patch *new_patch)
|
||||
void klp_unpatch_replaced_patches(struct klp_patch *new_patch)
|
||||
{
|
||||
struct klp_patch *old_patch, *tmp_patch;
|
||||
struct klp_patch *old_patch;
|
||||
|
||||
klp_for_each_patch_safe(old_patch, tmp_patch) {
|
||||
klp_for_each_patch(old_patch) {
|
||||
if (old_patch == new_patch)
|
||||
return;
|
||||
|
||||
old_patch->enabled = false;
|
||||
klp_unpatch_objects(old_patch);
|
||||
klp_free_patch_start(old_patch);
|
||||
schedule_work(&old_patch->free_work);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,9 @@ extern struct list_head klp_patches;
|
||||
#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_discard_replaced_patches(struct klp_patch *new_patch);
|
||||
void klp_free_patch_async(struct klp_patch *patch);
|
||||
void klp_free_replaced_patches_async(struct klp_patch *new_patch);
|
||||
void klp_unpatch_replaced_patches(struct klp_patch *new_patch);
|
||||
void klp_discard_nops(struct klp_patch *new_patch);
|
||||
|
||||
static inline bool klp_is_object_loaded(struct klp_object *obj)
|
||||
|
119
kernel/livepatch/state.c
Normal file
119
kernel/livepatch/state.c
Normal file
@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* system_state.c - State of the system modified by livepatches
|
||||
*
|
||||
* Copyright (C) 2019 SUSE
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/livepatch.h>
|
||||
#include "core.h"
|
||||
#include "state.h"
|
||||
#include "transition.h"
|
||||
|
||||
#define klp_for_each_state(patch, state) \
|
||||
for (state = patch->states; state && state->id; state++)
|
||||
|
||||
/**
|
||||
* klp_get_state() - get information about system state modified by
|
||||
* the given patch
|
||||
* @patch: livepatch that modifies the given system state
|
||||
* @id: custom identifier of the modified system state
|
||||
*
|
||||
* Checks whether the given patch modifies the given system state.
|
||||
*
|
||||
* The function can be called either from pre/post (un)patch
|
||||
* callbacks or from the kernel code added by the livepatch.
|
||||
*
|
||||
* Return: pointer to struct klp_state when found, otherwise NULL.
|
||||
*/
|
||||
struct klp_state *klp_get_state(struct klp_patch *patch, unsigned long id)
|
||||
{
|
||||
struct klp_state *state;
|
||||
|
||||
klp_for_each_state(patch, state) {
|
||||
if (state->id == id)
|
||||
return state;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(klp_get_state);
|
||||
|
||||
/**
|
||||
* klp_get_prev_state() - get information about system state modified by
|
||||
* the already installed livepatches
|
||||
* @id: custom identifier of the modified system state
|
||||
*
|
||||
* Checks whether already installed livepatches modify the given
|
||||
* system state.
|
||||
*
|
||||
* The same system state can be modified by more non-cumulative
|
||||
* livepatches. It is expected that the latest livepatch has
|
||||
* the most up-to-date information.
|
||||
*
|
||||
* The function can be called only during transition when a new
|
||||
* livepatch is being enabled or when such a transition is reverted.
|
||||
* It is typically called only from from pre/post (un)patch
|
||||
* callbacks.
|
||||
*
|
||||
* Return: pointer to the latest struct klp_state from already
|
||||
* installed livepatches, NULL when not found.
|
||||
*/
|
||||
struct klp_state *klp_get_prev_state(unsigned long id)
|
||||
{
|
||||
struct klp_patch *patch;
|
||||
struct klp_state *state, *last_state = NULL;
|
||||
|
||||
if (WARN_ON_ONCE(!klp_transition_patch))
|
||||
return NULL;
|
||||
|
||||
klp_for_each_patch(patch) {
|
||||
if (patch == klp_transition_patch)
|
||||
goto out;
|
||||
|
||||
state = klp_get_state(patch, id);
|
||||
if (state)
|
||||
last_state = state;
|
||||
}
|
||||
|
||||
out:
|
||||
return last_state;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(klp_get_prev_state);
|
||||
|
||||
/* Check if the patch is able to deal with the existing system state. */
|
||||
static bool klp_is_state_compatible(struct klp_patch *patch,
|
||||
struct klp_state *old_state)
|
||||
{
|
||||
struct klp_state *state;
|
||||
|
||||
state = klp_get_state(patch, old_state->id);
|
||||
|
||||
/* A cumulative livepatch must handle all already modified states. */
|
||||
if (!state)
|
||||
return !patch->replace;
|
||||
|
||||
return state->version >= old_state->version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the new livepatch will not break the existing system states.
|
||||
* Cumulative patches must handle all already modified states.
|
||||
* Non-cumulative patches can touch already modified states.
|
||||
*/
|
||||
bool klp_is_patch_compatible(struct klp_patch *patch)
|
||||
{
|
||||
struct klp_patch *old_patch;
|
||||
struct klp_state *old_state;
|
||||
|
||||
klp_for_each_patch(old_patch) {
|
||||
klp_for_each_state(old_patch, old_state) {
|
||||
if (!klp_is_state_compatible(patch, old_state))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
9
kernel/livepatch/state.h
Normal file
9
kernel/livepatch/state.h
Normal file
@ -0,0 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LIVEPATCH_STATE_H
|
||||
#define _LIVEPATCH_STATE_H
|
||||
|
||||
#include <linux/livepatch.h>
|
||||
|
||||
bool klp_is_patch_compatible(struct klp_patch *patch);
|
||||
|
||||
#endif /* _LIVEPATCH_STATE_H */
|
@ -78,7 +78,7 @@ static void klp_complete_transition(void)
|
||||
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
|
||||
|
||||
if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
|
||||
klp_discard_replaced_patches(klp_transition_patch);
|
||||
klp_unpatch_replaced_patches(klp_transition_patch);
|
||||
klp_discard_nops(klp_transition_patch);
|
||||
}
|
||||
|
||||
@ -446,14 +446,14 @@ void klp_try_complete_transition(void)
|
||||
klp_complete_transition();
|
||||
|
||||
/*
|
||||
* It would make more sense to free the patch in
|
||||
* It would make more sense to free the unused patches in
|
||||
* klp_complete_transition() but it is called also
|
||||
* from klp_cancel_transition().
|
||||
*/
|
||||
if (!patch->enabled) {
|
||||
klp_free_patch_start(patch);
|
||||
schedule_work(&patch->free_work);
|
||||
}
|
||||
if (!patch->enabled)
|
||||
klp_free_patch_async(patch);
|
||||
else if (patch->replace)
|
||||
klp_free_replaced_patches_async(patch);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,10 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
|
||||
test_klp_callbacks_busy.o \
|
||||
test_klp_callbacks_mod.o \
|
||||
test_klp_livepatch.o \
|
||||
test_klp_shadow_vars.o
|
||||
test_klp_shadow_vars.o \
|
||||
test_klp_state.o \
|
||||
test_klp_state2.o \
|
||||
test_klp_state3.o
|
||||
|
||||
# Target modules to be livepatched require CC_FLAGS_FTRACE
|
||||
CFLAGS_test_klp_callbacks_busy.o += $(CC_FLAGS_FTRACE)
|
||||
|
162
lib/livepatch/test_klp_state.c
Normal file
162
lib/livepatch/test_klp_state.c
Normal file
@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2019 SUSE
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/livepatch.h>
|
||||
|
||||
#define CONSOLE_LOGLEVEL_STATE 1
|
||||
/* Version 1 does not support migration. */
|
||||
#define CONSOLE_LOGLEVEL_STATE_VERSION 1
|
||||
|
||||
static const char *const module_state[] = {
|
||||
[MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
|
||||
[MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",
|
||||
[MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",
|
||||
[MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
|
||||
};
|
||||
|
||||
static void callback_info(const char *callback, struct klp_object *obj)
|
||||
{
|
||||
if (obj->mod)
|
||||
pr_info("%s: %s -> %s\n", callback, obj->mod->name,
|
||||
module_state[obj->mod->state]);
|
||||
else
|
||||
pr_info("%s: vmlinux\n", callback);
|
||||
}
|
||||
|
||||
static struct klp_patch patch;
|
||||
|
||||
static int allocate_loglevel_state(void)
|
||||
{
|
||||
struct klp_state *loglevel_state;
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return -EINVAL;
|
||||
|
||||
loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);
|
||||
if (!loglevel_state->data)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_info("%s: allocating space to store console_loglevel\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fix_console_loglevel(void)
|
||||
{
|
||||
struct klp_state *loglevel_state;
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return;
|
||||
|
||||
pr_info("%s: fixing console_loglevel\n", __func__);
|
||||
*(int *)loglevel_state->data = console_loglevel;
|
||||
console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
|
||||
}
|
||||
|
||||
static void restore_console_loglevel(void)
|
||||
{
|
||||
struct klp_state *loglevel_state;
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return;
|
||||
|
||||
pr_info("%s: restoring console_loglevel\n", __func__);
|
||||
console_loglevel = *(int *)loglevel_state->data;
|
||||
}
|
||||
|
||||
static void free_loglevel_state(void)
|
||||
{
|
||||
struct klp_state *loglevel_state;
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return;
|
||||
|
||||
pr_info("%s: freeing space for the stored console_loglevel\n",
|
||||
__func__);
|
||||
kfree(loglevel_state->data);
|
||||
}
|
||||
|
||||
/* Executed on object patching (ie, patch enablement) */
|
||||
static int pre_patch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
return allocate_loglevel_state();
|
||||
}
|
||||
|
||||
/* Executed on object unpatching (ie, patch disablement) */
|
||||
static void post_patch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
fix_console_loglevel();
|
||||
}
|
||||
|
||||
/* Executed on object unpatching (ie, patch disablement) */
|
||||
static void pre_unpatch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
restore_console_loglevel();
|
||||
}
|
||||
|
||||
/* Executed on object unpatching (ie, patch disablement) */
|
||||
static void post_unpatch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
free_loglevel_state();
|
||||
}
|
||||
|
||||
static struct klp_func no_funcs[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static struct klp_object objs[] = {
|
||||
{
|
||||
.name = NULL, /* vmlinux */
|
||||
.funcs = no_funcs,
|
||||
.callbacks = {
|
||||
.pre_patch = pre_patch_callback,
|
||||
.post_patch = post_patch_callback,
|
||||
.pre_unpatch = pre_unpatch_callback,
|
||||
.post_unpatch = post_unpatch_callback,
|
||||
},
|
||||
}, { }
|
||||
};
|
||||
|
||||
static struct klp_state states[] = {
|
||||
{
|
||||
.id = CONSOLE_LOGLEVEL_STATE,
|
||||
.version = CONSOLE_LOGLEVEL_STATE_VERSION,
|
||||
}, { }
|
||||
};
|
||||
|
||||
static struct klp_patch patch = {
|
||||
.mod = THIS_MODULE,
|
||||
.objs = objs,
|
||||
.states = states,
|
||||
.replace = true,
|
||||
};
|
||||
|
||||
static int test_klp_callbacks_demo_init(void)
|
||||
{
|
||||
return klp_enable_patch(&patch);
|
||||
}
|
||||
|
||||
static void test_klp_callbacks_demo_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(test_klp_callbacks_demo_init);
|
||||
module_exit(test_klp_callbacks_demo_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_INFO(livepatch, "Y");
|
||||
MODULE_AUTHOR("Petr Mladek <pmladek@suse.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: system state modification");
|
191
lib/livepatch/test_klp_state2.c
Normal file
191
lib/livepatch/test_klp_state2.c
Normal file
@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2019 SUSE
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/livepatch.h>
|
||||
|
||||
#define CONSOLE_LOGLEVEL_STATE 1
|
||||
/* Version 2 supports migration. */
|
||||
#define CONSOLE_LOGLEVEL_STATE_VERSION 2
|
||||
|
||||
static const char *const module_state[] = {
|
||||
[MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
|
||||
[MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",
|
||||
[MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",
|
||||
[MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
|
||||
};
|
||||
|
||||
static void callback_info(const char *callback, struct klp_object *obj)
|
||||
{
|
||||
if (obj->mod)
|
||||
pr_info("%s: %s -> %s\n", callback, obj->mod->name,
|
||||
module_state[obj->mod->state]);
|
||||
else
|
||||
pr_info("%s: vmlinux\n", callback);
|
||||
}
|
||||
|
||||
static struct klp_patch patch;
|
||||
|
||||
static int allocate_loglevel_state(void)
|
||||
{
|
||||
struct klp_state *loglevel_state, *prev_loglevel_state;
|
||||
|
||||
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
|
||||
if (prev_loglevel_state) {
|
||||
pr_info("%s: space to store console_loglevel already allocated\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return -EINVAL;
|
||||
|
||||
loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);
|
||||
if (!loglevel_state->data)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_info("%s: allocating space to store console_loglevel\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fix_console_loglevel(void)
|
||||
{
|
||||
struct klp_state *loglevel_state, *prev_loglevel_state;
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return;
|
||||
|
||||
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
|
||||
if (prev_loglevel_state) {
|
||||
pr_info("%s: taking over the console_loglevel change\n",
|
||||
__func__);
|
||||
loglevel_state->data = prev_loglevel_state->data;
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("%s: fixing console_loglevel\n", __func__);
|
||||
*(int *)loglevel_state->data = console_loglevel;
|
||||
console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
|
||||
}
|
||||
|
||||
static void restore_console_loglevel(void)
|
||||
{
|
||||
struct klp_state *loglevel_state, *prev_loglevel_state;
|
||||
|
||||
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
|
||||
if (prev_loglevel_state) {
|
||||
pr_info("%s: passing the console_loglevel change back to the old livepatch\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return;
|
||||
|
||||
pr_info("%s: restoring console_loglevel\n", __func__);
|
||||
console_loglevel = *(int *)loglevel_state->data;
|
||||
}
|
||||
|
||||
static void free_loglevel_state(void)
|
||||
{
|
||||
struct klp_state *loglevel_state, *prev_loglevel_state;
|
||||
|
||||
prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);
|
||||
if (prev_loglevel_state) {
|
||||
pr_info("%s: keeping space to store console_loglevel\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
|
||||
if (!loglevel_state)
|
||||
return;
|
||||
|
||||
pr_info("%s: freeing space for the stored console_loglevel\n",
|
||||
__func__);
|
||||
kfree(loglevel_state->data);
|
||||
}
|
||||
|
||||
/* Executed on object patching (ie, patch enablement) */
|
||||
static int pre_patch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
return allocate_loglevel_state();
|
||||
}
|
||||
|
||||
/* Executed on object unpatching (ie, patch disablement) */
|
||||
static void post_patch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
fix_console_loglevel();
|
||||
}
|
||||
|
||||
/* Executed on object unpatching (ie, patch disablement) */
|
||||
static void pre_unpatch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
restore_console_loglevel();
|
||||
}
|
||||
|
||||
/* Executed on object unpatching (ie, patch disablement) */
|
||||
static void post_unpatch_callback(struct klp_object *obj)
|
||||
{
|
||||
callback_info(__func__, obj);
|
||||
free_loglevel_state();
|
||||
}
|
||||
|
||||
static struct klp_func no_funcs[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static struct klp_object objs[] = {
|
||||
{
|
||||
.name = NULL, /* vmlinux */
|
||||
.funcs = no_funcs,
|
||||
.callbacks = {
|
||||
.pre_patch = pre_patch_callback,
|
||||
.post_patch = post_patch_callback,
|
||||
.pre_unpatch = pre_unpatch_callback,
|
||||
.post_unpatch = post_unpatch_callback,
|
||||
},
|
||||
}, { }
|
||||
};
|
||||
|
||||
static struct klp_state states[] = {
|
||||
{
|
||||
.id = CONSOLE_LOGLEVEL_STATE,
|
||||
.version = CONSOLE_LOGLEVEL_STATE_VERSION,
|
||||
}, { }
|
||||
};
|
||||
|
||||
static struct klp_patch patch = {
|
||||
.mod = THIS_MODULE,
|
||||
.objs = objs,
|
||||
.states = states,
|
||||
.replace = true,
|
||||
};
|
||||
|
||||
static int test_klp_callbacks_demo_init(void)
|
||||
{
|
||||
return klp_enable_patch(&patch);
|
||||
}
|
||||
|
||||
static void test_klp_callbacks_demo_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(test_klp_callbacks_demo_init);
|
||||
module_exit(test_klp_callbacks_demo_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_INFO(livepatch, "Y");
|
||||
MODULE_AUTHOR("Petr Mladek <pmladek@suse.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: system state modification");
|
5
lib/livepatch/test_klp_state3.c
Normal file
5
lib/livepatch/test_klp_state3.c
Normal file
@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2019 SUSE
|
||||
|
||||
/* The console loglevel fix is the same in the next cumulative patch. */
|
||||
#include "test_klp_state2.c"
|
@ -4,6 +4,7 @@ TEST_PROGS_EXTENDED := functions.sh
|
||||
TEST_PROGS := \
|
||||
test-livepatch.sh \
|
||||
test-callbacks.sh \
|
||||
test-shadow-vars.sh
|
||||
test-shadow-vars.sh \
|
||||
test-state.sh
|
||||
|
||||
include ../lib.mk
|
||||
|
1
tools/testing/selftests/livepatch/settings
Normal file
1
tools/testing/selftests/livepatch/settings
Normal file
@ -0,0 +1 @@
|
||||
timeout=0
|
180
tools/testing/selftests/livepatch/test-state.sh
Executable file
180
tools/testing/selftests/livepatch/test-state.sh
Executable file
@ -0,0 +1,180 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (C) 2019 SUSE
|
||||
|
||||
. $(dirname $0)/functions.sh
|
||||
|
||||
MOD_LIVEPATCH=test_klp_state
|
||||
MOD_LIVEPATCH2=test_klp_state2
|
||||
MOD_LIVEPATCH3=test_klp_state3
|
||||
|
||||
set_dynamic_debug
|
||||
|
||||
|
||||
# TEST: Loading and removing a module that modifies the system state
|
||||
|
||||
echo -n "TEST: system state modification ... "
|
||||
dmesg -C
|
||||
|
||||
load_lp $MOD_LIVEPATCH
|
||||
disable_lp $MOD_LIVEPATCH
|
||||
unload_lp $MOD_LIVEPATCH
|
||||
|
||||
check_result "% modprobe $MOD_LIVEPATCH
|
||||
livepatch: enabling patch '$MOD_LIVEPATCH'
|
||||
livepatch: '$MOD_LIVEPATCH': initializing patching transition
|
||||
$MOD_LIVEPATCH: pre_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH: allocate_loglevel_state: allocating space to store console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH': starting patching transition
|
||||
livepatch: '$MOD_LIVEPATCH': completing patching transition
|
||||
$MOD_LIVEPATCH: post_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH: fix_console_loglevel: fixing console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH': patching complete
|
||||
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
|
||||
livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
|
||||
$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH: restore_console_loglevel: restoring console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH': starting unpatching transition
|
||||
livepatch: '$MOD_LIVEPATCH': completing unpatching transition
|
||||
$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH: free_loglevel_state: freeing space for the stored console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH': unpatching complete
|
||||
% rmmod $MOD_LIVEPATCH"
|
||||
|
||||
|
||||
# TEST: Take over system state change by a cumulative patch
|
||||
|
||||
echo -n "TEST: taking over system state modification ... "
|
||||
dmesg -C
|
||||
|
||||
load_lp $MOD_LIVEPATCH
|
||||
load_lp $MOD_LIVEPATCH2
|
||||
unload_lp $MOD_LIVEPATCH
|
||||
disable_lp $MOD_LIVEPATCH2
|
||||
unload_lp $MOD_LIVEPATCH2
|
||||
|
||||
check_result "% modprobe $MOD_LIVEPATCH
|
||||
livepatch: enabling patch '$MOD_LIVEPATCH'
|
||||
livepatch: '$MOD_LIVEPATCH': initializing patching transition
|
||||
$MOD_LIVEPATCH: pre_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH: allocate_loglevel_state: allocating space to store console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH': starting patching transition
|
||||
livepatch: '$MOD_LIVEPATCH': completing patching transition
|
||||
$MOD_LIVEPATCH: post_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH: fix_console_loglevel: fixing console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH': patching complete
|
||||
% modprobe $MOD_LIVEPATCH2
|
||||
livepatch: enabling patch '$MOD_LIVEPATCH2'
|
||||
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
|
||||
$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: allocate_loglevel_state: space to store console_loglevel already allocated
|
||||
livepatch: '$MOD_LIVEPATCH2': starting patching transition
|
||||
livepatch: '$MOD_LIVEPATCH2': completing patching transition
|
||||
$MOD_LIVEPATCH2: post_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: fix_console_loglevel: taking over the console_loglevel change
|
||||
livepatch: '$MOD_LIVEPATCH2': patching complete
|
||||
% rmmod $MOD_LIVEPATCH
|
||||
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
|
||||
livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
|
||||
$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: restore_console_loglevel: restoring console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
|
||||
livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
|
||||
$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: free_loglevel_state: freeing space for the stored console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': unpatching complete
|
||||
% rmmod $MOD_LIVEPATCH2"
|
||||
|
||||
|
||||
# TEST: Take over system state change by a cumulative patch
|
||||
|
||||
echo -n "TEST: compatible cumulative livepatches ... "
|
||||
dmesg -C
|
||||
|
||||
load_lp $MOD_LIVEPATCH2
|
||||
load_lp $MOD_LIVEPATCH3
|
||||
unload_lp $MOD_LIVEPATCH2
|
||||
load_lp $MOD_LIVEPATCH2
|
||||
disable_lp $MOD_LIVEPATCH2
|
||||
unload_lp $MOD_LIVEPATCH2
|
||||
unload_lp $MOD_LIVEPATCH3
|
||||
|
||||
check_result "% modprobe $MOD_LIVEPATCH2
|
||||
livepatch: enabling patch '$MOD_LIVEPATCH2'
|
||||
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
|
||||
$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: allocate_loglevel_state: allocating space to store console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': starting patching transition
|
||||
livepatch: '$MOD_LIVEPATCH2': completing patching transition
|
||||
$MOD_LIVEPATCH2: post_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: fix_console_loglevel: fixing console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': patching complete
|
||||
% modprobe $MOD_LIVEPATCH3
|
||||
livepatch: enabling patch '$MOD_LIVEPATCH3'
|
||||
livepatch: '$MOD_LIVEPATCH3': initializing patching transition
|
||||
$MOD_LIVEPATCH3: pre_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH3: allocate_loglevel_state: space to store console_loglevel already allocated
|
||||
livepatch: '$MOD_LIVEPATCH3': starting patching transition
|
||||
livepatch: '$MOD_LIVEPATCH3': completing patching transition
|
||||
$MOD_LIVEPATCH3: post_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH3: fix_console_loglevel: taking over the console_loglevel change
|
||||
livepatch: '$MOD_LIVEPATCH3': patching complete
|
||||
% rmmod $MOD_LIVEPATCH2
|
||||
% modprobe $MOD_LIVEPATCH2
|
||||
livepatch: enabling patch '$MOD_LIVEPATCH2'
|
||||
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
|
||||
$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: allocate_loglevel_state: space to store console_loglevel already allocated
|
||||
livepatch: '$MOD_LIVEPATCH2': starting patching transition
|
||||
livepatch: '$MOD_LIVEPATCH2': completing patching transition
|
||||
$MOD_LIVEPATCH2: post_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: fix_console_loglevel: taking over the console_loglevel change
|
||||
livepatch: '$MOD_LIVEPATCH2': patching complete
|
||||
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
|
||||
livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
|
||||
$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: restore_console_loglevel: restoring console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
|
||||
livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
|
||||
$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: free_loglevel_state: freeing space for the stored console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': unpatching complete
|
||||
% rmmod $MOD_LIVEPATCH2
|
||||
% rmmod $MOD_LIVEPATCH3"
|
||||
|
||||
|
||||
# TEST: Failure caused by incompatible cumulative livepatches
|
||||
|
||||
echo -n "TEST: incompatible cumulative livepatches ... "
|
||||
dmesg -C
|
||||
|
||||
load_lp $MOD_LIVEPATCH2
|
||||
load_failing_mod $MOD_LIVEPATCH
|
||||
disable_lp $MOD_LIVEPATCH2
|
||||
unload_lp $MOD_LIVEPATCH2
|
||||
|
||||
check_result "% modprobe $MOD_LIVEPATCH2
|
||||
livepatch: enabling patch '$MOD_LIVEPATCH2'
|
||||
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
|
||||
$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: allocate_loglevel_state: allocating space to store console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': starting patching transition
|
||||
livepatch: '$MOD_LIVEPATCH2': completing patching transition
|
||||
$MOD_LIVEPATCH2: post_patch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: fix_console_loglevel: fixing console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': patching complete
|
||||
% modprobe $MOD_LIVEPATCH
|
||||
livepatch: Livepatch patch ($MOD_LIVEPATCH) is not compatible with the already installed livepatches.
|
||||
modprobe: ERROR: could not insert '$MOD_LIVEPATCH': Invalid argument
|
||||
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
|
||||
livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
|
||||
$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: restore_console_loglevel: restoring console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
|
||||
livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
|
||||
$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
|
||||
$MOD_LIVEPATCH2: free_loglevel_state: freeing space for the stored console_loglevel
|
||||
livepatch: '$MOD_LIVEPATCH2': unpatching complete
|
||||
% rmmod $MOD_LIVEPATCH2"
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue
Block a user