If a task sleeps in a set of patched functions uninterruptibly, it could
block the whole transition process indefinitely.  Thus it may be useful
to clear its TIF_PATCH_PENDING to allow the process to finish.

Admin can do that now by writing to force sysfs attribute in livepatch
sysfs directory. TIF_PATCH_PENDING is then cleared for all tasks and the
transition can finish successfully.

Important note! Use wisely. Admin must be sure that it is safe to
execute such action. This means that it must be checked that by doing so
the consistency model guarantees are not violated.

Signed-off-by: Miroslav Benes <mbe...@suse.cz>
---
 Documentation/ABI/testing/sysfs-kernel-livepatch |  6 +++++-
 Documentation/livepatch/livepatch.txt            |  6 +++++-
 kernel/livepatch/core.c                          |  4 +++-
 kernel/livepatch/transition.c                    | 25 ++++++++++++++++++++++++
 kernel/livepatch/transition.h                    |  1 +
 5 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-kernel-livepatch 
b/Documentation/ABI/testing/sysfs-kernel-livepatch
index 45f4e3551d27..c1604a45bf84 100644
--- a/Documentation/ABI/testing/sysfs-kernel-livepatch
+++ b/Documentation/ABI/testing/sysfs-kernel-livepatch
@@ -17,11 +17,15 @@ Contact:    live-patch...@vger.kernel.org
                existing transition.
 
                Reading from the file returns all available operations, which
-               may be "signal" (signalling remaining tasks).
+               may be "signal" (signalling remaining tasks) and "force" (force
+               transition process to finish).
 
                Writing one of the strings to the file executes the operation.
                "signal" sends a signal to all remaining blocking tasks.
 
+               "force" clears TIF_PATCH_PENDING flag of all tasks and thus
+               forces the tasks to the patched state.
+
 What:          /sys/kernel/livepatch/<patch>
 Date:          Nov 2014
 KernelVersion: 3.19.0
diff --git a/Documentation/livepatch/livepatch.txt 
b/Documentation/livepatch/livepatch.txt
index 343b0bfa1b9f..7626d1b947c2 100644
--- a/Documentation/livepatch/livepatch.txt
+++ b/Documentation/livepatch/livepatch.txt
@@ -183,7 +183,11 @@ attribute. Reading from the file returns all available 
operations. Writing one
 of the strings to the file executes the operation. "signal" is available for
 signalling all remaining blocking tasks. This is an alternative for
 SIGSTOP/SIGCONT approach mentioned in the previous paragraph. It should also be
-less harmful to the system.
+less harmful to the system. "force" clears TIF_PATCH_PENDING flag of all tasks
+and thus forces the tasks to the patched state. Important note! Use "force"
+wisely. Administrator must be sure that it is safe to execute such action. This
+means that it must be checked that by doing so the consistency model guarantees
+are not violated.
 
 
 3.1 Adding consistency model support to new architectures
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index a359340c924d..e759af6e4393 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -452,7 +452,7 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
 static ssize_t force_show(struct kobject *kobj,
                          struct kobj_attribute *attr, char *buf)
 {
-       return sprintf(buf, "signal\n");
+       return sprintf(buf, "signal force\n");
 }
 
 static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr,
@@ -470,6 +470,8 @@ static ssize_t force_store(struct kobject *kobj, struct 
kobj_attribute *attr,
 
        if (!memcmp("signal", buf, min(sizeof("signal")-1, count)))
                klp_force_signals();
+       else if (!memcmp("force", buf, min(sizeof("force")-1, count)))
+               klp_force_transitions();
        else
                return -EINVAL;
 
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index 0d8be69be8b1..d69ec3be7b8e 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -617,3 +617,28 @@ void klp_force_signals(void)
        }
        read_unlock(&tasklist_lock);
 }
+
+/*
+ * Drop TIF_PATCH_PENDING of all tasks on admin's request. This forces an
+ * existing transition to finish.
+ *
+ * NOTE: klp_update_patch_state(task) requires the task to be inactive or
+ * 'current'. This is not the case here and the consistency model could be
+ * broken. Administrator, who is the only one to execute the
+ * klp_force_transitions(), has to be aware of this.
+ */
+void klp_force_transitions(void)
+{
+       struct task_struct *g, *task;
+       unsigned int cpu;
+
+       pr_warn("forcing remaining tasks to the patched state\n");
+
+       read_lock(&tasklist_lock);
+       for_each_process_thread(g, task)
+               klp_update_patch_state(task);
+       read_unlock(&tasklist_lock);
+
+       for_each_possible_cpu(cpu)
+               klp_update_patch_state(idle_task(cpu));
+}
diff --git a/kernel/livepatch/transition.h b/kernel/livepatch/transition.h
index 6c480057539a..da3be48d36bb 100644
--- a/kernel/livepatch/transition.h
+++ b/kernel/livepatch/transition.h
@@ -11,5 +11,6 @@ void klp_start_transition(void);
 void klp_try_complete_transition(void);
 void klp_reverse_transition(void);
 void klp_force_signals(void);
+void klp_force_transitions(void);
 
 #endif /* _LIVEPATCH_TRANSITION_H */
-- 
2.13.3

Reply via email to