From: george anzinger The attached patch should fix the system hang referenced below (works on my system) and also the deliver to a specific thread issue. I don't have a test for this last issue so feed back is desired. Once you pass it I will send it in. Ulrich Drepper wrote: > > http://people.redhat.com/drepper/t-overr.bz2 > > is a statically linked binary. You'll see that the program hangs when > it tries to disarm the timer. include/linux/sched.h | 1 kernel/posix-timers.c | 68 +++++++++++++++++++++++++++++++------------------- kernel/signal.c | 2 - 3 files changed, 45 insertions(+), 26 deletions(-) diff -puN include/linux/sched.h~posix-timer-hang-fix include/linux/sched.h --- 25/include/linux/sched.h~posix-timer-hang-fix 2003-04-02 23:46:41.000000000 -0800 +++ 25-akpm/include/linux/sched.h 2003-04-02 23:46:41.000000000 -0800 @@ -546,6 +546,7 @@ extern void block_all_signals(int (*noti extern void unblock_all_signals(void); extern void release_task(struct task_struct * p); extern int send_sig_info(int, struct siginfo *, struct task_struct *); +extern int specific_send_sig_info(int, struct siginfo *, struct task_struct *); extern int force_sig_info(int, struct siginfo *, struct task_struct *); extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp); extern int kill_pg_info(int, struct siginfo *, pid_t); diff -puN kernel/posix-timers.c~posix-timer-hang-fix kernel/posix-timers.c --- 25/kernel/posix-timers.c~posix-timer-hang-fix 2003-04-02 23:46:41.000000000 -0800 +++ 25-akpm/kernel/posix-timers.c 2003-04-02 23:46:41.000000000 -0800 @@ -77,6 +77,20 @@ static spinlock_t idr_lock = SPIN_LOCK_U # define timer_active(tmr) BARFY // error to use outside of SMP # define set_timer_inactive(tmr) do { } while (0) #endif + +/* + * For some reason mips/mips64 define the SIGEV constants plus 128. + * Here we define a mask to get rid of the common bits. The + * optimizer should make this costless to all but mips. + */ +#if (ARCH == mips) || (ARCH == mips64) +#define MIPS_SIGEV ~(SIGEV_NONE & \ + SIGEV_SIGNAL & \ + SIGEV_THREAD & \ + SIGEV_THREAD_ID) +#else +#define MIPS_SIGEV (int)-1 +#endif /* * The timer ID is turned into a timer address by idr_find(). * Verifying a valid ID consists of: @@ -225,10 +239,9 @@ static void schedule_next_timer(struct k struct now_struct now; /* Set up the timer for the next interval (if there is one) */ - if (!timr->it_incr) { - set_timer_inactive(timr); + if (!timr->it_incr) return; - } + posix_get_now(&now); do { posix_bump_timer(timr); @@ -258,7 +271,7 @@ void do_schedule_next_timer(struct sigin timr = lock_timer(info->si_tid, &flags); - if (!timr || !timr->it_requeue_pending) + if (!timr || timr->it_requeue_pending != info->si_sys_private) goto exit; schedule_next_timer(timr); @@ -276,7 +289,17 @@ exit: * indicating that the signal was either not queued or was queued * without an info block. In this case, we will not get a call back to * do_schedule_next_timer() so we do it here. This should be rare... + + * An interesting problem can occure if, while a signal, and thus a call + * back is pending, the timer is rearmed, i.e. stopped and restarted. + * We then need to sort out the call back and do the right thing. What + * we do is to put a counter in the info block and match it with the + * timers copy on the call back. If they don't match, we just ignore + * the call back. Note that we do allow the timer to be deleted while + * a signal is pending. The standard says we can allow that signal to + * be delivered, and we do. */ +static int pendcount; static void timer_notify_task(struct k_itimer *timr) { @@ -291,12 +314,20 @@ static void timer_notify_task(struct k_i info.si_code = SI_TIMER; info.si_tid = timr->it_id; info.si_value = timr->it_sigev_value; - if (!timr->it_incr) - set_timer_inactive(timr); - else - timr->it_requeue_pending = info.si_sys_private = 1; - - ret = send_sig_info(info.si_signo, &info, timr->it_process); + if( !++pendcount ) ++pendcount; + if (timr->it_incr){ + /* + * Don't allow a call back counter of zero... + */ + if( !++pendcount ) + ++pendcount; + timr->it_requeue_pending = info.si_sys_private = pendcount; + } + if( timr->it_sigev_notify & SIGEV_THREAD_ID & MIPS_SIGEV){ + ret = specific_send_sig_info(info.si_signo, &info, + timr->it_process); + }else + ret = send_sig_info(info.si_signo, &info, timr->it_process); switch (ret) { default: @@ -332,23 +363,11 @@ static void posix_timer_fn(unsigned long unsigned long flags; spin_lock_irqsave(&timr->it_lock, flags); + set_timer_inactive(timr); timer_notify_task(timr); unlock_timer(timr, flags); } -/* - * For some reason mips/mips64 define the SIGEV constants plus 128. - * Here we define a mask to get rid of the common bits. The - * optimizer should make this costless to all but mips. - */ -#if (ARCH == mips) || (ARCH == mips64) -#define MIPS_SIGEV ~(SIGEV_NONE & \ - SIGEV_SIGNAL & \ - SIGEV_THREAD & \ - SIGEV_THREAD_ID) -#else -#define MIPS_SIGEV (int)-1 -#endif static inline struct task_struct * good_sigevent(sigevent_t * event) { @@ -847,8 +866,7 @@ static inline int do_timer_delete(struct { timer->it_incr = 0; #ifdef CONFIG_SMP - if (timer_active(timer) && - !del_timer(&timer->it_timer) && !timer->it_requeue_pending) + if (timer_active(timer) && !del_timer(&timer->it_timer)) /* * It can only be active if on an other cpu. Since * we have cleared the interval stuff above, it should diff -puN kernel/signal.c~posix-timer-hang-fix kernel/signal.c --- 25/kernel/signal.c~posix-timer-hang-fix 2003-04-02 23:46:41.000000000 -0800 +++ 25-akpm/kernel/signal.c 2003-04-02 23:46:41.000000000 -0800 @@ -749,7 +749,7 @@ out_set: (((sig) < SIGRTMIN) && sigismember(&(sigptr)->signal, (sig))) -static int +int specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t) { int ret = 0; _