From: Ingo Molnar This is another generic fallout from the voluntary-preempt patchset: a cleanup of the cond_resched() infrastructure, in preparation of the latency reduction patches. The changes: - uninline cond_resched() - this makes the footprint smaller, especially once the number of cond_resched() points increase. - add a 'was rescheduled' return value to cond_resched. This makes it symmetric to cond_resched_lock() and later latency reduction patches rely on the ability to tell whether there was any preemption. - make cond_resched() more robust by using the same mechanism as preempt_kernel(): by using PREEMPT_ACTIVE. This preserves the task's state - e.g. if the task is in TASK_ZOMBIE but gets preempted via cond_resched() just prior scheduling off then this approach preserves TASK_ZOMBIE. - the patch also adds need_lockbreak() which critical sections can use to detect lock-break requests. I've tested the patch on x86 SMP and UP. Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton --- 25-akpm/include/linux/sched.h | 23 ++++++++++++++++------- 25-akpm/kernel/sched.c | 23 +++++++++++++++++------ 2 files changed, 33 insertions(+), 13 deletions(-) diff -puN include/linux/sched.h~preempt-cleanup include/linux/sched.h --- 25/include/linux/sched.h~preempt-cleanup 2004-12-03 20:56:34.156567600 -0800 +++ 25-akpm/include/linux/sched.h 2004-12-03 20:56:34.163566536 -0800 @@ -1066,15 +1066,24 @@ static inline int need_resched(void) return unlikely(test_thread_flag(TIF_NEED_RESCHED)); } -extern void __cond_resched(void); -static inline void cond_resched(void) -{ - if (need_resched()) - __cond_resched(); -} - +/* + * cond_resched() and cond_resched_lock(): latency reduction via + * explicit rescheduling in places that are safe. The return + * value indicates whether a reschedule was done in fact. + */ +extern int cond_resched(void); extern int cond_resched_lock(spinlock_t * lock); +/* + * Does a critical section need to be broken due to another + * task waiting?: + */ +#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP) +# define need_lockbreak(lock) ((lock)->break_lock) +#else +# define need_lockbreak(lock) 0 +#endif + /* Reevaluate whether the task has signals pending delivery. This is required every time the blocked sigset_t changes. callers must hold sighand->siglock. */ diff -puN kernel/sched.c~preempt-cleanup kernel/sched.c --- 25/kernel/sched.c~preempt-cleanup 2004-12-03 20:56:34.158567296 -0800 +++ 25-akpm/kernel/sched.c 2004-12-03 20:56:34.167565928 -0800 @@ -3451,13 +3451,25 @@ asmlinkage long sys_sched_yield(void) return 0; } -void __sched __cond_resched(void) +static inline void __cond_resched(void) { - set_current_state(TASK_RUNNING); - schedule(); + do { + preempt_count() += PREEMPT_ACTIVE; + schedule(); + preempt_count() -= PREEMPT_ACTIVE; + } while (need_resched()); } -EXPORT_SYMBOL(__cond_resched); +int __sched cond_resched(void) +{ + if (need_resched()) { + __cond_resched(); + return 1; + } + return 0; +} + +EXPORT_SYMBOL(cond_resched); /* * cond_resched_lock() - if a reschedule is pending, drop the given lock, @@ -3480,8 +3492,7 @@ int cond_resched_lock(spinlock_t * lock) if (need_resched()) { _raw_spin_unlock(lock); preempt_enable_no_resched(); - set_current_state(TASK_RUNNING); - schedule(); + __cond_resched(); spin_lock(lock); return 1; } _