[IA64] fix interrupt masking for pending works on kernel leave
Hidetoshi Seto [Fri, 9 May 2008 06:26:35 +0000 (15:26 +0900)]
[Bug-fix for "[BUG?][2.6.25-mm1] sleeping during IRQ disabled"]

This patch does:
 - enable interrupts before calling schedule() as same as others, ex. x86
 - enable interrupts during ia64_do_signal() and ia64_sync_krbs()
 - do_notify_resume_user() is still called with interrupts disabled, since
   we can take short path of fsys_mode if-statement quickly.
 - pfm_handle_work() is also called with interrupts disabled, since
   it can deal interrupt mask within itself.
 - fix/add some comments/notes

Reported-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>

arch/ia64/kernel/entry.S
arch/ia64/kernel/process.c

index e49ad8c..ef6b031 100644 (file)
@@ -1156,6 +1156,9 @@ skip_rbs_switch:
         *      r31 = current->thread_info->flags
         * On exit:
         *      p6 = TRUE if work-pending-check needs to be redone
+        *
+        * Interrupts are disabled on entry, reenabled depend on work, and
+        * disabled on exit.
         */
 .work_pending_syscall:
        add r2=-8,r2
@@ -1170,8 +1173,8 @@ skip_rbs_switch:
 (pKStk) dep r21=-1,r0,PREEMPT_ACTIVE_BIT,1
        ;;
 (pKStk) st4 [r20]=r21
-       ssm psr.i               // enable interrupts
 #endif
+       ssm psr.i               // enable interrupts
        br.call.spnt.many rp=schedule
 .ret9: cmp.eq p6,p0=r0,r0                              // p6 <- 1
        rsm psr.i               // disable interrupts
@@ -1234,9 +1237,12 @@ GLOBAL_ENTRY(ia64_invoke_schedule_tail)
 END(ia64_invoke_schedule_tail)
 
        /*
-        * Setup stack and call do_notify_resume_user().  Note that pSys and pNonSys need to
-        * be set up by the caller.  We declare 8 input registers so the system call
-        * args get preserved, in case we need to restart a system call.
+        * Setup stack and call do_notify_resume_user(), keeping interrupts
+        * disabled.
+        *
+        * Note that pSys and pNonSys need to be set up by the caller.
+        * We declare 8 input registers so the system call args get preserved,
+        * in case we need to restart a system call.
         */
 ENTRY(notify_resume_user)
        .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)
index 58dcfac..a3a34b4 100644 (file)
@@ -167,11 +167,18 @@ void tsk_clear_notify_resume(struct task_struct *tsk)
        clear_ti_thread_flag(task_thread_info(tsk), TIF_NOTIFY_RESUME);
 }
 
+/*
+ * do_notify_resume_user():
+ *     Called from notify_resume_user at entry.S, with interrupts disabled.
+ */
 void
-do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall)
+do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
 {
        if (fsys_mode(current, &scr->pt)) {
-               /* defer signal-handling etc. until we return to privilege-level 0.  */
+               /*
+                * defer signal-handling etc. until we return to
+                * privilege-level 0.
+                */
                if (!ia64_psr(&scr->pt)->lp)
                        ia64_psr(&scr->pt)->lp = 1;
                return;
@@ -179,16 +186,26 @@ do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall
 
 #ifdef CONFIG_PERFMON
        if (current->thread.pfm_needs_checking)
+               /*
+                * Note: pfm_handle_work() allow us to call it with interrupts
+                * disabled, and may enable interrupts within the function.
+                */
                pfm_handle_work();
 #endif
 
        /* deal with pending signal delivery */
-       if (test_thread_flag(TIF_SIGPENDING))
+       if (test_thread_flag(TIF_SIGPENDING)) {
+               local_irq_enable();     /* force interrupt enable */
                ia64_do_signal(scr, in_syscall);
+       }
 
        /* copy user rbs to kernel rbs */
-       if (unlikely(test_thread_flag(TIF_RESTORE_RSE)))
+       if (unlikely(test_thread_flag(TIF_RESTORE_RSE))) {
+               local_irq_enable();     /* force interrupt enable */
                ia64_sync_krbs();
+       }
+
+       local_irq_disable();    /* force interrupt disable */
 }
 
 static int pal_halt        = 1;