diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index b9aeaca26d3a..67e2d4d14ae6 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
@@ -574,6 +574,7 @@ static void pfault_interrupt(struct ext_code ext_code, | |||
574 | tsk->thread.pfault_wait = 0; | 574 | tsk->thread.pfault_wait = 0; |
575 | list_del(&tsk->thread.list); | 575 | list_del(&tsk->thread.list); |
576 | wake_up_process(tsk); | 576 | wake_up_process(tsk); |
577 | put_task_struct(tsk); | ||
577 | } else { | 578 | } else { |
578 | /* Completion interrupt was faster than initial | 579 | /* Completion interrupt was faster than initial |
579 | * interrupt. Set pfault_wait to -1 so the initial | 580 | * interrupt. Set pfault_wait to -1 so the initial |
@@ -588,14 +589,22 @@ static void pfault_interrupt(struct ext_code ext_code, | |||
588 | put_task_struct(tsk); | 589 | put_task_struct(tsk); |
589 | } else { | 590 | } else { |
590 | /* signal bit not set -> a real page is missing. */ | 591 | /* signal bit not set -> a real page is missing. */ |
591 | if (tsk->thread.pfault_wait == -1) { | 592 | if (tsk->thread.pfault_wait == 1) { |
593 | /* Already on the list with a reference: put to sleep */ | ||
594 | set_task_state(tsk, TASK_UNINTERRUPTIBLE); | ||
595 | set_tsk_need_resched(tsk); | ||
596 | } else if (tsk->thread.pfault_wait == -1) { | ||
592 | /* Completion interrupt was faster than the initial | 597 | /* Completion interrupt was faster than the initial |
593 | * interrupt (pfault_wait == -1). Set pfault_wait | 598 | * interrupt (pfault_wait == -1). Set pfault_wait |
594 | * back to zero and exit. */ | 599 | * back to zero and exit. */ |
595 | tsk->thread.pfault_wait = 0; | 600 | tsk->thread.pfault_wait = 0; |
596 | } else { | 601 | } else { |
597 | /* Initial interrupt arrived before completion | 602 | /* Initial interrupt arrived before completion |
598 | * interrupt. Let the task sleep. */ | 603 | * interrupt. Let the task sleep. |
604 | * An extra task reference is needed since a different | ||
605 | * cpu may set the task state to TASK_RUNNING again | ||
606 | * before the scheduler is reached. */ | ||
607 | get_task_struct(tsk); | ||
599 | tsk->thread.pfault_wait = 1; | 608 | tsk->thread.pfault_wait = 1; |
600 | list_add(&tsk->thread.list, &pfault_list); | 609 | list_add(&tsk->thread.list, &pfault_list); |
601 | set_task_state(tsk, TASK_UNINTERRUPTIBLE); | 610 | set_task_state(tsk, TASK_UNINTERRUPTIBLE); |
@@ -620,6 +629,7 @@ static int __cpuinit pfault_cpu_notify(struct notifier_block *self, | |||
620 | list_del(&thread->list); | 629 | list_del(&thread->list); |
621 | tsk = container_of(thread, struct task_struct, thread); | 630 | tsk = container_of(thread, struct task_struct, thread); |
622 | wake_up_process(tsk); | 631 | wake_up_process(tsk); |
632 | put_task_struct(tsk); | ||
623 | } | 633 | } |
624 | spin_unlock_irq(&pfault_lock); | 634 | spin_unlock_irq(&pfault_lock); |
625 | break; | 635 | break; |