diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-kernel-livepatch | 12 | ||||
| -rw-r--r-- | Documentation/livepatch/livepatch.txt | 16 | ||||
| -rw-r--r-- | kernel/livepatch/core.c | 32 | ||||
| -rw-r--r-- | kernel/livepatch/transition.c | 82 | ||||
| -rw-r--r-- | kernel/livepatch/transition.h | 1 |
5 files changed, 48 insertions, 95 deletions
diff --git a/Documentation/ABI/testing/sysfs-kernel-livepatch b/Documentation/ABI/testing/sysfs-kernel-livepatch index dac7e1e62a8b..85db352f68f9 100644 --- a/Documentation/ABI/testing/sysfs-kernel-livepatch +++ b/Documentation/ABI/testing/sysfs-kernel-livepatch | |||
| @@ -33,18 +33,6 @@ Description: | |||
| 33 | An attribute which indicates whether the patch is currently in | 33 | An attribute which indicates whether the patch is currently in |
| 34 | transition. | 34 | transition. |
| 35 | 35 | ||
| 36 | What: /sys/kernel/livepatch/<patch>/signal | ||
| 37 | Date: Nov 2017 | ||
| 38 | KernelVersion: 4.15.0 | ||
| 39 | Contact: live-patching@vger.kernel.org | ||
| 40 | Description: | ||
| 41 | A writable attribute that allows administrator to affect the | ||
| 42 | course of an existing transition. Writing 1 sends a fake | ||
| 43 | signal to all remaining blocking tasks. The fake signal | ||
| 44 | means that no proper signal is delivered (there is no data in | ||
| 45 | signal pending structures). Tasks are interrupted or woken up, | ||
| 46 | and forced to change their patched state. | ||
| 47 | |||
| 48 | What: /sys/kernel/livepatch/<patch>/force | 36 | What: /sys/kernel/livepatch/<patch>/force |
| 49 | Date: Nov 2017 | 37 | Date: Nov 2017 |
| 50 | KernelVersion: 4.15.0 | 38 | KernelVersion: 4.15.0 |
diff --git a/Documentation/livepatch/livepatch.txt b/Documentation/livepatch/livepatch.txt index 407e0f03dc99..4627b41ff02e 100644 --- a/Documentation/livepatch/livepatch.txt +++ b/Documentation/livepatch/livepatch.txt | |||
| @@ -158,13 +158,11 @@ If a patch is in transition, this file shows 0 to indicate the task is | |||
| 158 | unpatched and 1 to indicate it's patched. Otherwise, if no patch is in | 158 | unpatched and 1 to indicate it's patched. Otherwise, if no patch is in |
| 159 | transition, it shows -1. Any tasks which are blocking the transition | 159 | transition, it shows -1. Any tasks which are blocking the transition |
| 160 | can be signaled with SIGSTOP and SIGCONT to force them to change their | 160 | can be signaled with SIGSTOP and SIGCONT to force them to change their |
| 161 | patched state. This may be harmful to the system though. | 161 | patched state. This may be harmful to the system though. Sending a fake signal |
| 162 | /sys/kernel/livepatch/<patch>/signal attribute provides a better alternative. | 162 | to all remaining blocking tasks is a better alternative. No proper signal is |
| 163 | Writing 1 to the attribute sends a fake signal to all remaining blocking | 163 | actually delivered (there is no data in signal pending structures). Tasks are |
| 164 | tasks. No proper signal is actually delivered (there is no data in signal | 164 | interrupted or woken up, and forced to change their patched state. The fake |
| 165 | pending structures). Tasks are interrupted or woken up, and forced to change | 165 | signal is automatically sent every 15 seconds. |
| 166 | their patched state. Despite the sysfs attribute the fake signal is also sent | ||
| 167 | every 15 seconds automatically. | ||
| 168 | 166 | ||
| 169 | Administrator can also affect a transition through | 167 | Administrator can also affect a transition through |
| 170 | /sys/kernel/livepatch/<patch>/force attribute. Writing 1 there clears | 168 | /sys/kernel/livepatch/<patch>/force attribute. Writing 1 there clears |
| @@ -412,8 +410,8 @@ Information about the registered patches can be found under | |||
| 412 | /sys/kernel/livepatch. The patches could be enabled and disabled | 410 | /sys/kernel/livepatch. The patches could be enabled and disabled |
| 413 | by writing there. | 411 | by writing there. |
| 414 | 412 | ||
| 415 | /sys/kernel/livepatch/<patch>/signal and /sys/kernel/livepatch/<patch>/force | 413 | /sys/kernel/livepatch/<patch>/force attributes allow administrator to affect a |
| 416 | attributes allow administrator to affect a patching operation. | 414 | patching operation. |
| 417 | 415 | ||
| 418 | See Documentation/ABI/testing/sysfs-kernel-livepatch for more details. | 416 | See Documentation/ABI/testing/sysfs-kernel-livepatch for more details. |
| 419 | 417 | ||
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index adca5cf07f7e..fe1993399823 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
| @@ -313,7 +313,6 @@ static int klp_write_object_relocations(struct module *pmod, | |||
| 313 | * /sys/kernel/livepatch/<patch> | 313 | * /sys/kernel/livepatch/<patch> |
| 314 | * /sys/kernel/livepatch/<patch>/enabled | 314 | * /sys/kernel/livepatch/<patch>/enabled |
| 315 | * /sys/kernel/livepatch/<patch>/transition | 315 | * /sys/kernel/livepatch/<patch>/transition |
| 316 | * /sys/kernel/livepatch/<patch>/signal | ||
| 317 | * /sys/kernel/livepatch/<patch>/force | 316 | * /sys/kernel/livepatch/<patch>/force |
| 318 | * /sys/kernel/livepatch/<patch>/<object> | 317 | * /sys/kernel/livepatch/<patch>/<object> |
| 319 | * /sys/kernel/livepatch/<patch>/<object>/<function,sympos> | 318 | * /sys/kernel/livepatch/<patch>/<object>/<function,sympos> |
| @@ -382,35 +381,6 @@ static ssize_t transition_show(struct kobject *kobj, | |||
| 382 | patch == klp_transition_patch); | 381 | patch == klp_transition_patch); |
| 383 | } | 382 | } |
| 384 | 383 | ||
| 385 | static ssize_t signal_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
| 386 | const char *buf, size_t count) | ||
| 387 | { | ||
| 388 | struct klp_patch *patch; | ||
| 389 | int ret; | ||
| 390 | bool val; | ||
| 391 | |||
| 392 | ret = kstrtobool(buf, &val); | ||
| 393 | if (ret) | ||
| 394 | return ret; | ||
| 395 | |||
| 396 | if (!val) | ||
| 397 | return count; | ||
| 398 | |||
| 399 | mutex_lock(&klp_mutex); | ||
| 400 | |||
| 401 | patch = container_of(kobj, struct klp_patch, kobj); | ||
| 402 | if (patch != klp_transition_patch) { | ||
| 403 | mutex_unlock(&klp_mutex); | ||
| 404 | return -EINVAL; | ||
| 405 | } | ||
| 406 | |||
| 407 | klp_send_signals(); | ||
| 408 | |||
| 409 | mutex_unlock(&klp_mutex); | ||
| 410 | |||
| 411 | return count; | ||
| 412 | } | ||
| 413 | |||
| 414 | static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, | 384 | static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, |
| 415 | const char *buf, size_t count) | 385 | const char *buf, size_t count) |
| 416 | { | 386 | { |
| @@ -442,12 +412,10 @@ static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, | |||
| 442 | 412 | ||
| 443 | static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled); | 413 | static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled); |
| 444 | static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition); | 414 | static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition); |
| 445 | static struct kobj_attribute signal_kobj_attr = __ATTR_WO(signal); | ||
| 446 | static struct kobj_attribute force_kobj_attr = __ATTR_WO(force); | 415 | static struct kobj_attribute force_kobj_attr = __ATTR_WO(force); |
| 447 | static struct attribute *klp_patch_attrs[] = { | 416 | static struct attribute *klp_patch_attrs[] = { |
| 448 | &enabled_kobj_attr.attr, | 417 | &enabled_kobj_attr.attr, |
| 449 | &transition_kobj_attr.attr, | 418 | &transition_kobj_attr.attr, |
| 450 | &signal_kobj_attr.attr, | ||
| 451 | &force_kobj_attr.attr, | 419 | &force_kobj_attr.attr, |
| 452 | NULL | 420 | NULL |
| 453 | }; | 421 | }; |
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c index ea7697bb753e..183b2086ba03 100644 --- a/kernel/livepatch/transition.c +++ b/kernel/livepatch/transition.c | |||
| @@ -348,6 +348,47 @@ done: | |||
| 348 | } | 348 | } |
| 349 | 349 | ||
| 350 | /* | 350 | /* |
| 351 | * Sends a fake signal to all non-kthread tasks with TIF_PATCH_PENDING set. | ||
| 352 | * Kthreads with TIF_PATCH_PENDING set are woken up. | ||
| 353 | */ | ||
| 354 | static void klp_send_signals(void) | ||
| 355 | { | ||
| 356 | struct task_struct *g, *task; | ||
| 357 | |||
| 358 | if (klp_signals_cnt == SIGNALS_TIMEOUT) | ||
| 359 | pr_notice("signaling remaining tasks\n"); | ||
| 360 | |||
| 361 | read_lock(&tasklist_lock); | ||
| 362 | for_each_process_thread(g, task) { | ||
| 363 | if (!klp_patch_pending(task)) | ||
| 364 | continue; | ||
| 365 | |||
| 366 | /* | ||
| 367 | * There is a small race here. We could see TIF_PATCH_PENDING | ||
| 368 | * set and decide to wake up a kthread or send a fake signal. | ||
| 369 | * Meanwhile the task could migrate itself and the action | ||
| 370 | * would be meaningless. It is not serious though. | ||
| 371 | */ | ||
| 372 | if (task->flags & PF_KTHREAD) { | ||
| 373 | /* | ||
| 374 | * Wake up a kthread which sleeps interruptedly and | ||
| 375 | * still has not been migrated. | ||
| 376 | */ | ||
| 377 | wake_up_state(task, TASK_INTERRUPTIBLE); | ||
| 378 | } else { | ||
| 379 | /* | ||
| 380 | * Send fake signal to all non-kthread tasks which are | ||
| 381 | * still not migrated. | ||
| 382 | */ | ||
| 383 | spin_lock_irq(&task->sighand->siglock); | ||
| 384 | signal_wake_up(task, 0); | ||
| 385 | spin_unlock_irq(&task->sighand->siglock); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | read_unlock(&tasklist_lock); | ||
| 389 | } | ||
| 390 | |||
| 391 | /* | ||
| 351 | * Try to switch all remaining tasks to the target patch state by walking the | 392 | * Try to switch all remaining tasks to the target patch state by walking the |
| 352 | * stacks of sleeping tasks and looking for any to-be-patched or | 393 | * stacks of sleeping tasks and looking for any to-be-patched or |
| 353 | * to-be-unpatched functions. If such functions are found, the task can't be | 394 | * to-be-unpatched functions. If such functions are found, the task can't be |
| @@ -587,47 +628,6 @@ void klp_copy_process(struct task_struct *child) | |||
| 587 | } | 628 | } |
| 588 | 629 | ||
| 589 | /* | 630 | /* |
| 590 | * Sends a fake signal to all non-kthread tasks with TIF_PATCH_PENDING set. | ||
| 591 | * Kthreads with TIF_PATCH_PENDING set are woken up. | ||
| 592 | */ | ||
| 593 | void klp_send_signals(void) | ||
| 594 | { | ||
| 595 | struct task_struct *g, *task; | ||
| 596 | |||
| 597 | if (klp_signals_cnt == SIGNALS_TIMEOUT) | ||
| 598 | pr_notice("signaling remaining tasks\n"); | ||
| 599 | |||
| 600 | read_lock(&tasklist_lock); | ||
| 601 | for_each_process_thread(g, task) { | ||
| 602 | if (!klp_patch_pending(task)) | ||
| 603 | continue; | ||
| 604 | |||
| 605 | /* | ||
| 606 | * There is a small race here. We could see TIF_PATCH_PENDING | ||
| 607 | * set and decide to wake up a kthread or send a fake signal. | ||
| 608 | * Meanwhile the task could migrate itself and the action | ||
| 609 | * would be meaningless. It is not serious though. | ||
| 610 | */ | ||
| 611 | if (task->flags & PF_KTHREAD) { | ||
| 612 | /* | ||
| 613 | * Wake up a kthread which sleeps interruptedly and | ||
| 614 | * still has not been migrated. | ||
| 615 | */ | ||
| 616 | wake_up_state(task, TASK_INTERRUPTIBLE); | ||
| 617 | } else { | ||
| 618 | /* | ||
| 619 | * Send fake signal to all non-kthread tasks which are | ||
| 620 | * still not migrated. | ||
| 621 | */ | ||
| 622 | spin_lock_irq(&task->sighand->siglock); | ||
| 623 | signal_wake_up(task, 0); | ||
| 624 | spin_unlock_irq(&task->sighand->siglock); | ||
| 625 | } | ||
| 626 | } | ||
| 627 | read_unlock(&tasklist_lock); | ||
| 628 | } | ||
| 629 | |||
| 630 | /* | ||
| 631 | * Drop TIF_PATCH_PENDING of all tasks on admin's request. This forces an | 631 | * Drop TIF_PATCH_PENDING of all tasks on admin's request. This forces an |
| 632 | * existing transition to finish. | 632 | * existing transition to finish. |
| 633 | * | 633 | * |
diff --git a/kernel/livepatch/transition.h b/kernel/livepatch/transition.h index f9d0bc016067..322db16233de 100644 --- a/kernel/livepatch/transition.h +++ b/kernel/livepatch/transition.h | |||
| @@ -11,7 +11,6 @@ void klp_cancel_transition(void); | |||
| 11 | void klp_start_transition(void); | 11 | void klp_start_transition(void); |
| 12 | void klp_try_complete_transition(void); | 12 | void klp_try_complete_transition(void); |
| 13 | void klp_reverse_transition(void); | 13 | void klp_reverse_transition(void); |
| 14 | void klp_send_signals(void); | ||
| 15 | void klp_force_transition(void); | 14 | void klp_force_transition(void); |
| 16 | 15 | ||
| 17 | #endif /* _LIVEPATCH_TRANSITION_H */ | 16 | #endif /* _LIVEPATCH_TRANSITION_H */ |
