]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/livepatch/core.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux.git] / kernel / livepatch / core.c
index ab4a4606d19b761caaabb82d39c632b37b0c3e3b..c3512e7e0801253d4ab03a450f3cd91090fa3ea4 100644 (file)
@@ -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 @@ int klp_enable_patch(struct klp_patch *patch)
 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);
        }
 }