--- v2
+++ v4
@@ -1,67 +1,523 @@
-Add the TIF_PATCH_PENDING thread flag to enable the new livepatch
-per-task consistency model for powerpc. The bit getting set indicates
-the thread has a pending patch which needs to be applied when the thread
-exits the kernel.
-
-The bit is included in the _TIF_USER_WORK_MASK macro so that
-do_notify_resume() and klp_patch_task() get called when the bit is set.
+Move functions related to the actual patching of functions and objects
+into a new patch.c file.
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
+Acked-by: Miroslav Benes <mbenes@suse.cz>
+Reviewed-by: Petr Mladek <pmladek@suse.com>
+Reviewed-by: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com>
---
- arch/powerpc/include/asm/thread_info.h | 4 +++-
- arch/powerpc/kernel/signal.c | 4 ++++
- 2 files changed, 7 insertions(+), 1 deletion(-)
+ kernel/livepatch/Makefile | 2 +-
+ kernel/livepatch/core.c | 202 +------------------------------------------
+ kernel/livepatch/patch.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++
+ kernel/livepatch/patch.h | 32 +++++++
+ 4 files changed, 247 insertions(+), 202 deletions(-)
+ create mode 100644 kernel/livepatch/patch.c
+ create mode 100644 kernel/livepatch/patch.h
-diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
-index 8febc3f..df262ca 100644
---- a/arch/powerpc/include/asm/thread_info.h
-+++ b/arch/powerpc/include/asm/thread_info.h
-@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void)
- TIF_NEED_RESCHED */
- #define TIF_32BIT 4 /* 32 bit binary */
- #define TIF_RESTORE_TM 5 /* need to restore TM FP/VEC/VSX */
-+#define TIF_PATCH_PENDING 6 /* pending live patching update */
- #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
- #define TIF_SINGLESTEP 8 /* singlestepping active */
- #define TIF_NOHZ 9 /* in adaptive nohz mode */
-@@ -111,6 +112,7 @@ static inline struct thread_info *current_thread_info(void)
- #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
- #define _TIF_32BIT (1<<TIF_32BIT)
- #define _TIF_RESTORE_TM (1<<TIF_RESTORE_TM)
-+#define _TIF_PATCH_PENDING (1<<TIF_PATCH_PENDING)
- #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
- #define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
- #define _TIF_SECCOMP (1<<TIF_SECCOMP)
-@@ -127,7 +129,7 @@ static inline struct thread_info *current_thread_info(void)
-
- #define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
- _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
-- _TIF_RESTORE_TM)
-+ _TIF_RESTORE_TM | _TIF_PATCH_PENDING)
- #define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR)
-
- /* Bits in local_flags */
-diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
-index cb64d6f..844497b 100644
---- a/arch/powerpc/kernel/signal.c
-+++ b/arch/powerpc/kernel/signal.c
-@@ -14,6 +14,7 @@
- #include <linux/uprobes.h>
- #include <linux/key.h>
- #include <linux/context_tracking.h>
+diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile
+index e8780c0..e136dad 100644
+--- a/kernel/livepatch/Makefile
++++ b/kernel/livepatch/Makefile
+@@ -1,3 +1,3 @@
+ obj-$(CONFIG_LIVEPATCH) += livepatch.o
+
+-livepatch-objs := core.o
++livepatch-objs := core.o patch.o
+diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
+index 47ed643..6a137e1 100644
+--- a/kernel/livepatch/core.c
++++ b/kernel/livepatch/core.c
+@@ -24,32 +24,13 @@
+ #include <linux/kernel.h>
+ #include <linux/mutex.h>
+ #include <linux/slab.h>
+-#include <linux/ftrace.h>
+ #include <linux/list.h>
+ #include <linux/kallsyms.h>
+ #include <linux/livepatch.h>
+ #include <linux/elf.h>
+ #include <linux/moduleloader.h>
+ #include <asm/cacheflush.h>
+-
+-/**
+- * struct klp_ops - structure for tracking registered ftrace ops structs
+- *
+- * A single ftrace_ops is shared between all enabled replacement functions
+- * (klp_func structs) which have the same old_addr. This allows the switch
+- * between function versions to happen instantaneously by updating the klp_ops
+- * struct's func_stack list. The winner is the klp_func at the top of the
+- * func_stack (front of the list).
+- *
+- * @node: node for the global klp_ops list
+- * @func_stack: list head for the stack of klp_func's (active func is on top)
+- * @fops: registered ftrace ops struct
+- */
+-struct klp_ops {
+- struct list_head node;
+- struct list_head func_stack;
+- struct ftrace_ops fops;
+-};
++#include "patch.h"
+
+ /*
+ * The klp_mutex protects the global lists and state transitions of any
+@@ -60,28 +41,12 @@ struct klp_ops {
+ static DEFINE_MUTEX(klp_mutex);
+
+ static LIST_HEAD(klp_patches);
+-static LIST_HEAD(klp_ops);
+
+ static struct kobject *klp_root_kobj;
+
+ /* TODO: temporary stub */
+ void klp_update_patch_state(struct task_struct *task) {}
+
+-static struct klp_ops *klp_find_ops(unsigned long old_addr)
+-{
+- struct klp_ops *ops;
+- struct klp_func *func;
+-
+- list_for_each_entry(ops, &klp_ops, node) {
+- func = list_first_entry(&ops->func_stack, struct klp_func,
+- stack_node);
+- if (func->old_addr == old_addr)
+- return ops;
+- }
+-
+- return NULL;
+-}
+-
+ static bool klp_is_module(struct klp_object *obj)
+ {
+ return obj->name;
+@@ -314,171 +279,6 @@ static int klp_write_object_relocations(struct module *pmod,
+ return ret;
+ }
+
+-static void notrace klp_ftrace_handler(unsigned long ip,
+- unsigned long parent_ip,
+- struct ftrace_ops *fops,
+- struct pt_regs *regs)
+-{
+- struct klp_ops *ops;
+- struct klp_func *func;
+-
+- ops = container_of(fops, struct klp_ops, fops);
+-
+- rcu_read_lock();
+- func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
+- stack_node);
+- if (WARN_ON_ONCE(!func))
+- goto unlock;
+-
+- klp_arch_set_pc(regs, (unsigned long)func->new_func);
+-unlock:
+- rcu_read_unlock();
+-}
+-
+-/*
+- * Convert a function address into the appropriate ftrace location.
+- *
+- * Usually this is just the address of the function, but on some architectures
+- * it's more complicated so allow them to provide a custom behaviour.
+- */
+-#ifndef klp_get_ftrace_location
+-static unsigned long klp_get_ftrace_location(unsigned long faddr)
+-{
+- return faddr;
+-}
+-#endif
+-
+-static void klp_unpatch_func(struct klp_func *func)
+-{
+- struct klp_ops *ops;
+-
+- if (WARN_ON(!func->patched))
+- return;
+- if (WARN_ON(!func->old_addr))
+- return;
+-
+- ops = klp_find_ops(func->old_addr);
+- if (WARN_ON(!ops))
+- return;
+-
+- if (list_is_singular(&ops->func_stack)) {
+- unsigned long ftrace_loc;
+-
+- ftrace_loc = klp_get_ftrace_location(func->old_addr);
+- if (WARN_ON(!ftrace_loc))
+- return;
+-
+- WARN_ON(unregister_ftrace_function(&ops->fops));
+- WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
+-
+- list_del_rcu(&func->stack_node);
+- list_del(&ops->node);
+- kfree(ops);
+- } else {
+- list_del_rcu(&func->stack_node);
+- }
+-
+- func->patched = false;
+-}
+-
+-static int klp_patch_func(struct klp_func *func)
+-{
+- struct klp_ops *ops;
+- int ret;
+-
+- if (WARN_ON(!func->old_addr))
+- return -EINVAL;
+-
+- if (WARN_ON(func->patched))
+- return -EINVAL;
+-
+- ops = klp_find_ops(func->old_addr);
+- if (!ops) {
+- unsigned long ftrace_loc;
+-
+- ftrace_loc = klp_get_ftrace_location(func->old_addr);
+- if (!ftrace_loc) {
+- pr_err("failed to find location for function '%s'\n",
+- func->old_name);
+- return -EINVAL;
+- }
+-
+- ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+- if (!ops)
+- return -ENOMEM;
+-
+- ops->fops.func = klp_ftrace_handler;
+- ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |
+- FTRACE_OPS_FL_DYNAMIC |
+- FTRACE_OPS_FL_IPMODIFY;
+-
+- list_add(&ops->node, &klp_ops);
+-
+- INIT_LIST_HEAD(&ops->func_stack);
+- list_add_rcu(&func->stack_node, &ops->func_stack);
+-
+- ret = ftrace_set_filter_ip(&ops->fops, ftrace_loc, 0, 0);
+- if (ret) {
+- pr_err("failed to set ftrace filter for function '%s' (%d)\n",
+- func->old_name, ret);
+- goto err;
+- }
+-
+- ret = register_ftrace_function(&ops->fops);
+- if (ret) {
+- pr_err("failed to register ftrace handler for function '%s' (%d)\n",
+- func->old_name, ret);
+- ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0);
+- goto err;
+- }
+-
+-
+- } else {
+- list_add_rcu(&func->stack_node, &ops->func_stack);
+- }
+-
+- func->patched = true;
+-
+- return 0;
+-
+-err:
+- list_del_rcu(&func->stack_node);
+- list_del(&ops->node);
+- kfree(ops);
+- return ret;
+-}
+-
+-static void klp_unpatch_object(struct klp_object *obj)
+-{
+- struct klp_func *func;
+-
+- klp_for_each_func(obj, func)
+- if (func->patched)
+- klp_unpatch_func(func);
+-
+- obj->patched = false;
+-}
+-
+-static int klp_patch_object(struct klp_object *obj)
+-{
+- struct klp_func *func;
+- int ret;
+-
+- if (WARN_ON(obj->patched))
+- return -EINVAL;
+-
+- klp_for_each_func(obj, func) {
+- ret = klp_patch_func(func);
+- if (ret) {
+- klp_unpatch_object(obj);
+- return ret;
+- }
+- }
+- obj->patched = true;
+-
+- return 0;
+-}
+-
+ static int __klp_disable_patch(struct klp_patch *patch)
+ {
+ struct klp_object *obj;
+diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
+new file mode 100644
+index 0000000..5efa262
+--- /dev/null
++++ b/kernel/livepatch/patch.c
+@@ -0,0 +1,213 @@
++/*
++ * patch.c - livepatch patching functions
++ *
++ * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
++ * Copyright (C) 2014 SUSE
++ * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
+#include <linux/livepatch.h>
- #include <asm/hw_breakpoint.h>
- #include <asm/uaccess.h>
- #include <asm/unistd.h>
-@@ -159,6 +160,9 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
- tracehook_notify_resume(regs);
- }
-
-+ if (thread_info_flags & _TIF_PATCH_PENDING)
-+ klp_patch_task(current);
-+
- user_enter();
- }
-
++#include <linux/list.h>
++#include <linux/ftrace.h>
++#include <linux/rculist.h>
++#include <linux/slab.h>
++#include <linux/bug.h>
++#include <linux/printk.h>
++#include "patch.h"
++
++static LIST_HEAD(klp_ops);
++
++struct klp_ops *klp_find_ops(unsigned long old_addr)
++{
++ struct klp_ops *ops;
++ struct klp_func *func;
++
++ list_for_each_entry(ops, &klp_ops, node) {
++ func = list_first_entry(&ops->func_stack, struct klp_func,
++ stack_node);
++ if (func->old_addr == old_addr)
++ return ops;
++ }
++
++ return NULL;
++}
++
++static void notrace klp_ftrace_handler(unsigned long ip,
++ unsigned long parent_ip,
++ struct ftrace_ops *fops,
++ struct pt_regs *regs)
++{
++ struct klp_ops *ops;
++ struct klp_func *func;
++
++ ops = container_of(fops, struct klp_ops, fops);
++
++ rcu_read_lock();
++ func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
++ stack_node);
++ if (WARN_ON_ONCE(!func))
++ goto unlock;
++
++ klp_arch_set_pc(regs, (unsigned long)func->new_func);
++unlock:
++ rcu_read_unlock();
++}
++
++/*
++ * Convert a function address into the appropriate ftrace location.
++ *
++ * Usually this is just the address of the function, but on some architectures
++ * it's more complicated so allow them to provide a custom behaviour.
++ */
++#ifndef klp_get_ftrace_location
++static unsigned long klp_get_ftrace_location(unsigned long faddr)
++{
++ return faddr;
++}
++#endif
++
++static void klp_unpatch_func(struct klp_func *func)
++{
++ struct klp_ops *ops;
++
++ if (WARN_ON(!func->patched))
++ return;
++ if (WARN_ON(!func->old_addr))
++ return;
++
++ ops = klp_find_ops(func->old_addr);
++ if (WARN_ON(!ops))
++ return;
++
++ if (list_is_singular(&ops->func_stack)) {
++ unsigned long ftrace_loc;
++
++ ftrace_loc = klp_get_ftrace_location(func->old_addr);
++ if (WARN_ON(!ftrace_loc))
++ return;
++
++ WARN_ON(unregister_ftrace_function(&ops->fops));
++ WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
++
++ list_del_rcu(&func->stack_node);
++ list_del(&ops->node);
++ kfree(ops);
++ } else {
++ list_del_rcu(&func->stack_node);
++ }
++
++ func->patched = false;
++}
++
++static int klp_patch_func(struct klp_func *func)
++{
++ struct klp_ops *ops;
++ int ret;
++
++ if (WARN_ON(!func->old_addr))
++ return -EINVAL;
++
++ if (WARN_ON(func->patched))
++ return -EINVAL;
++
++ ops = klp_find_ops(func->old_addr);
++ if (!ops) {
++ unsigned long ftrace_loc;
++
++ ftrace_loc = klp_get_ftrace_location(func->old_addr);
++ if (!ftrace_loc) {
++ pr_err("failed to find location for function '%s'\n",
++ func->old_name);
++ return -EINVAL;
++ }
++
++ ops = kzalloc(sizeof(*ops), GFP_KERNEL);
++ if (!ops)
++ return -ENOMEM;
++
++ ops->fops.func = klp_ftrace_handler;
++ ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |
++ FTRACE_OPS_FL_DYNAMIC |
++ FTRACE_OPS_FL_IPMODIFY;
++
++ list_add(&ops->node, &klp_ops);
++
++ INIT_LIST_HEAD(&ops->func_stack);
++ list_add_rcu(&func->stack_node, &ops->func_stack);
++
++ ret = ftrace_set_filter_ip(&ops->fops, ftrace_loc, 0, 0);
++ if (ret) {
++ pr_err("failed to set ftrace filter for function '%s' (%d)\n",
++ func->old_name, ret);
++ goto err;
++ }
++
++ ret = register_ftrace_function(&ops->fops);
++ if (ret) {
++ pr_err("failed to register ftrace handler for function '%s' (%d)\n",
++ func->old_name, ret);
++ ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0);
++ goto err;
++ }
++
++
++ } else {
++ list_add_rcu(&func->stack_node, &ops->func_stack);
++ }
++
++ func->patched = true;
++
++ return 0;
++
++err:
++ list_del_rcu(&func->stack_node);
++ list_del(&ops->node);
++ kfree(ops);
++ return ret;
++}
++
++void klp_unpatch_object(struct klp_object *obj)
++{
++ struct klp_func *func;
++
++ klp_for_each_func(obj, func)
++ if (func->patched)
++ klp_unpatch_func(func);
++
++ obj->patched = false;
++}
++
++int klp_patch_object(struct klp_object *obj)
++{
++ struct klp_func *func;
++ int ret;
++
++ if (WARN_ON(obj->patched))
++ return -EINVAL;
++
++ klp_for_each_func(obj, func) {
++ ret = klp_patch_func(func);
++ if (ret) {
++ klp_unpatch_object(obj);
++ return ret;
++ }
++ }
++ obj->patched = true;
++
++ return 0;
++}
+diff --git a/kernel/livepatch/patch.h b/kernel/livepatch/patch.h
+new file mode 100644
+index 0000000..2d0cce0
+--- /dev/null
++++ b/kernel/livepatch/patch.h
+@@ -0,0 +1,32 @@
++#ifndef _LIVEPATCH_PATCH_H
++#define _LIVEPATCH_PATCH_H
++
++#include <linux/livepatch.h>
++#include <linux/list.h>
++#include <linux/ftrace.h>
++
++/**
++ * struct klp_ops - structure for tracking registered ftrace ops structs
++ *
++ * A single ftrace_ops is shared between all enabled replacement functions
++ * (klp_func structs) which have the same old_addr. This allows the switch
++ * between function versions to happen instantaneously by updating the klp_ops
++ * struct's func_stack list. The winner is the klp_func at the top of the
++ * func_stack (front of the list).
++ *
++ * @node: node for the global klp_ops list
++ * @func_stack: list head for the stack of klp_func's (active func is on top)
++ * @fops: registered ftrace ops struct
++ */
++struct klp_ops {
++ struct list_head node;
++ struct list_head func_stack;
++ struct ftrace_ops fops;
++};
++
++struct klp_ops *klp_find_ops(unsigned long old_addr);
++
++int klp_patch_object(struct klp_object *obj);
++void klp_unpatch_object(struct klp_object *obj);
++
++#endif /* _LIVEPATCH_PATCH_H */
--
-2.4.11
+2.7.4