Merge remote-tracking branch 'origin/develop-3.0' into develop-3.0-jb
[firefly-linux-kernel-4.4.55.git] / arch / s390 / mm / fault.c
index fe103e891e7a0eb32dc7c67fc0cea3217bd4b6e3..6903d441068eaad2e012d94cb6eeffd32549cda6 100644 (file)
@@ -36,7 +36,6 @@
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/mmu_context.h>
-#include <asm/compat.h>
 #include "../kernel/entry.h"
 
 #ifndef CONFIG_64BIT
@@ -568,6 +567,7 @@ static void pfault_interrupt(unsigned int ext_int_code,
                        tsk->thread.pfault_wait = 0;
                        list_del(&tsk->thread.list);
                        wake_up_process(tsk);
+                       put_task_struct(tsk);
                } else {
                        /* Completion interrupt was faster than initial
                         * interrupt. Set pfault_wait to -1 so the initial
@@ -577,14 +577,22 @@ static void pfault_interrupt(unsigned int ext_int_code,
                put_task_struct(tsk);
        } else {
                /* signal bit not set -> a real page is missing. */
-               if (tsk->thread.pfault_wait == -1) {
+               if (tsk->thread.pfault_wait == 1) {
+                       /* Already on the list with a reference: put to sleep */
+                       set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+                       set_tsk_need_resched(tsk);
+               } else if (tsk->thread.pfault_wait == -1) {
                        /* Completion interrupt was faster than the initial
                         * interrupt (pfault_wait == -1). Set pfault_wait
                         * back to zero and exit. */
                        tsk->thread.pfault_wait = 0;
                } else {
                        /* Initial interrupt arrived before completion
-                        * interrupt. Let the task sleep. */
+                        * interrupt. Let the task sleep.
+                        * An extra task reference is needed since a different
+                        * cpu may set the task state to TASK_RUNNING again
+                        * before the scheduler is reached. */
+                       get_task_struct(tsk);
                        tsk->thread.pfault_wait = 1;
                        list_add(&tsk->thread.list, &pfault_list);
                        set_task_state(tsk, TASK_UNINTERRUPTIBLE);
@@ -609,6 +617,7 @@ static int __cpuinit pfault_cpu_notify(struct notifier_block *self,
                        list_del(&thread->list);
                        tsk = container_of(thread, struct task_struct, thread);
                        wake_up_process(tsk);
+                       put_task_struct(tsk);
                }
                spin_unlock_irq(&pfault_lock);
                break;