From: Mikael Pettersson - When a task is resumed, check if suspend recorded that an overflow interrupt is pending. If so, handle the overflow and deliver the signal. - Split interrupt handler in two parts: one used only for hardware interrupts, and one also used for software-generated interrupts. - Change signal generation code to not wake up the target task (== current). Avoids lockups when the interrupt/signal is generated from switch_to(). - Remove obsolete comment at vperfctr_suspend(). Signed-off-by: Mikael Pettersson Signed-off-by: Andrew Morton --- 25-akpm/drivers/perfctr/virtual.c | 41 +++++++++++++++++++++++++++++++++----- 1 files changed, 36 insertions(+), 5 deletions(-) diff -puN drivers/perfctr/virtual.c~perfctr-virtual-updates drivers/perfctr/virtual.c --- 25/drivers/perfctr/virtual.c~perfctr-virtual-updates 2004-11-11 01:44:53.805265304 -0800 +++ 25-akpm/drivers/perfctr/virtual.c 2004-11-11 01:44:53.810264544 -0800 @@ -48,6 +48,7 @@ struct vperfctr { #ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT static void vperfctr_ihandler(unsigned long pc); +static void vperfctr_handle_overflow(struct task_struct*, struct vperfctr*); static inline void vperfctr_set_ihandler(void) { @@ -197,8 +198,6 @@ static void put_vperfctr(struct vperfctr /* PRE: IS_RUNNING(perfctr) * Suspend the counters. - * XXX: When called from switch_to(), perfctr belongs to 'prev' - * but current is 'next'. */ static inline void vperfctr_suspend(struct vperfctr *perfctr) { @@ -220,6 +219,18 @@ static inline void vperfctr_resume(struc vperfctr_reset_sampling_timer(perfctr); } +static inline void vperfctr_resume_with_overflow_check(struct vperfctr *perfctr) +{ +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT + if (perfctr->cpu_state.pending_interrupt) { + perfctr->cpu_state.pending_interrupt = 0; + vperfctr_handle_overflow(current, perfctr); + return; + } +#endif + vperfctr_resume(perfctr); +} + /* Sample the counters but do not suspend them. */ static void vperfctr_sample(struct vperfctr *perfctr) { @@ -236,8 +247,6 @@ static void vperfctr_ihandler(unsigned l { struct task_struct *tsk = current; struct vperfctr *perfctr; - unsigned int pmc_mask; - siginfo_t si; perfctr = tsk->thread.perfctr; if (!perfctr) { @@ -251,6 +260,16 @@ static void vperfctr_ihandler(unsigned l return; } vperfctr_suspend(perfctr); + vperfctr_handle_overflow(tsk, perfctr); +} + +static void vperfctr_handle_overflow(struct task_struct *tsk, + struct vperfctr *perfctr) +{ + unsigned int pmc_mask; + siginfo_t si; + sigset_t old_blocked; + pmc_mask = perfctr_cpu_identify_overflow(&perfctr->cpu_state); if (!pmc_mask) { printk(KERN_ERR "%s: BUG! pid %d has unidentifiable overflow source\n", @@ -269,8 +288,20 @@ static void vperfctr_ihandler(unsigned l si.si_errno = 0; si.si_code = SI_PMC_OVF; si.si_pmc_ovf_mask = pmc_mask; + + /* deliver signal without waking up the receiver */ + spin_lock_irq(&tsk->sighand->siglock); + old_blocked = tsk->blocked; + sigaddset(&tsk->blocked, si.si_signo); + spin_unlock_irq(&tsk->sighand->siglock); + if (!send_sig_info(si.si_signo, &si, tsk)) send_sig(si.si_signo, tsk, 1); + + spin_lock_irq(&tsk->sighand->siglock); + tsk->blocked = old_blocked; + recalc_sigpending(); + spin_unlock_irq(&tsk->sighand->siglock); } #endif @@ -341,7 +372,7 @@ void __vperfctr_resume(struct vperfctr * return; } #endif - vperfctr_resume(perfctr); + vperfctr_resume_with_overflow_check(perfctr); } } _