diff options
Diffstat (limited to 'kernel/posix-cpu-timers.c')
| -rw-r--r-- | kernel/posix-cpu-timers.c | 110 | 
1 files changed, 63 insertions, 47 deletions
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index b3f3edc475d..bf374fceb39 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -91,7 +91,7 @@ static inline union cpu_time_count cpu_time_sub(clockid_t which_clock,   * Update expiry time from increment, and increase overrun count,   * given the current clock sample.   */ -static inline void bump_cpu_timer(struct k_itimer *timer, +static void bump_cpu_timer(struct k_itimer *timer,  				  union cpu_time_count now)  {  	int i; @@ -110,7 +110,7 @@ static inline void bump_cpu_timer(struct k_itimer *timer,  		for (i = 0; incr < delta - incr; i++)  			incr = incr << 1;  		for (; i >= 0; incr >>= 1, i--) { -			if (delta <= incr) +			if (delta < incr)  				continue;  			timer->it.cpu.expires.sched += incr;  			timer->it_overrun += 1 << i; @@ -128,7 +128,7 @@ static inline void bump_cpu_timer(struct k_itimer *timer,  		for (i = 0; cputime_lt(incr, cputime_sub(delta, incr)); i++)  			     incr = cputime_add(incr, incr);  		for (; i >= 0; incr = cputime_halve(incr), i--) { -			if (cputime_le(delta, incr)) +			if (cputime_lt(delta, incr))  				continue;  			timer->it.cpu.expires.cpu =  				cputime_add(timer->it.cpu.expires.cpu, incr); @@ -380,28 +380,31 @@ int posix_cpu_timer_create(struct k_itimer *new_timer)  int posix_cpu_timer_del(struct k_itimer *timer)  {  	struct task_struct *p = timer->it.cpu.task; +	int ret = 0; -	if (timer->it.cpu.firing) -		return TIMER_RETRY; - -	if (unlikely(p == NULL)) -		return 0; +	if (likely(p != NULL)) { +		read_lock(&tasklist_lock); +		if (unlikely(p->signal == NULL)) { +			/* +			 * We raced with the reaping of the task. +			 * The deletion should have cleared us off the list. +			 */ +			BUG_ON(!list_empty(&timer->it.cpu.entry)); +		} else { +			spin_lock(&p->sighand->siglock); +			if (timer->it.cpu.firing) +				ret = TIMER_RETRY; +			else +				list_del(&timer->it.cpu.entry); +			spin_unlock(&p->sighand->siglock); +		} +		read_unlock(&tasklist_lock); -	spin_lock(&p->sighand->siglock); -	if (!list_empty(&timer->it.cpu.entry)) { -		/* -		 * Take us off the task's timer list.  We don't need to -		 * take tasklist_lock and check for the task being reaped. -		 * If it was reaped, it already called posix_cpu_timers_exit -		 * and posix_cpu_timers_exit_group to clear all the timers -		 * that pointed to it. -		 */ -		list_del(&timer->it.cpu.entry); -		put_task_struct(p); +		if (!ret) +			put_task_struct(p);  	} -	spin_unlock(&p->sighand->siglock); -	return 0; +	return ret;  }  /* @@ -418,8 +421,6 @@ static void cleanup_timers(struct list_head *head,  	cputime_t ptime = cputime_add(utime, stime);  	list_for_each_entry_safe(timer, next, head, entry) { -		put_task_struct(timer->task); -		timer->task = NULL;  		list_del_init(&timer->entry);  		if (cputime_lt(timer->expires.cpu, ptime)) {  			timer->expires.cpu = cputime_zero; @@ -431,8 +432,6 @@ static void cleanup_timers(struct list_head *head,  	++head;  	list_for_each_entry_safe(timer, next, head, entry) { -		put_task_struct(timer->task); -		timer->task = NULL;  		list_del_init(&timer->entry);  		if (cputime_lt(timer->expires.cpu, utime)) {  			timer->expires.cpu = cputime_zero; @@ -444,8 +443,6 @@ static void cleanup_timers(struct list_head *head,  	++head;  	list_for_each_entry_safe(timer, next, head, entry) { -		put_task_struct(timer->task); -		timer->task = NULL;  		list_del_init(&timer->entry);  		if (timer->expires.sched < sched_time) {  			timer->expires.sched = 0; @@ -489,6 +486,9 @@ static void process_timer_rebalance(struct task_struct *p,   	struct task_struct *t = p;  	unsigned int nthreads = atomic_read(&p->signal->live); +	if (!nthreads) +		return; +  	switch (clock_idx) {  	default:  		BUG(); @@ -497,7 +497,7 @@ static void process_timer_rebalance(struct task_struct *p,  		left = cputime_div(cputime_sub(expires.cpu, val.cpu),  				   nthreads);  		do { -			if (!unlikely(t->exit_state)) { +			if (!unlikely(t->flags & PF_EXITING)) {  				ticks = cputime_add(prof_ticks(t), left);  				if (cputime_eq(t->it_prof_expires,  					       cputime_zero) || @@ -512,7 +512,7 @@ static void process_timer_rebalance(struct task_struct *p,  		left = cputime_div(cputime_sub(expires.cpu, val.cpu),  				   nthreads);  		do { -			if (!unlikely(t->exit_state)) { +			if (!unlikely(t->flags & PF_EXITING)) {  				ticks = cputime_add(virt_ticks(t), left);  				if (cputime_eq(t->it_virt_expires,  					       cputime_zero) || @@ -527,7 +527,7 @@ static void process_timer_rebalance(struct task_struct *p,  		nsleft = expires.sched - val.sched;  		do_div(nsleft, nthreads);  		do { -			if (!unlikely(t->exit_state)) { +			if (!unlikely(t->flags & PF_EXITING)) {  				ns = t->sched_time + nsleft;  				if (t->it_sched_expires == 0 ||  				    t->it_sched_expires > ns) { @@ -566,6 +566,9 @@ static void arm_timer(struct k_itimer *timer, union cpu_time_count now)  	struct cpu_timer_list *next;  	unsigned long i; +	if (CPUCLOCK_PERTHREAD(timer->it_clock)	&& (p->flags & PF_EXITING)) +		return; +  	head = (CPUCLOCK_PERTHREAD(timer->it_clock) ?  		p->cpu_timers : p->signal->cpu_timers);  	head += CPUCLOCK_WHICH(timer->it_clock); @@ -576,17 +579,15 @@ static void arm_timer(struct k_itimer *timer, union cpu_time_count now)  	listpos = head;  	if (CPUCLOCK_WHICH(timer->it_clock) == CPUCLOCK_SCHED) {  		list_for_each_entry(next, head, entry) { -			if (next->expires.sched > nt->expires.sched) { -				listpos = &next->entry; +			if (next->expires.sched > nt->expires.sched)  				break; -			} +			listpos = &next->entry;  		}  	} else {  		list_for_each_entry(next, head, entry) { -			if (cputime_gt(next->expires.cpu, nt->expires.cpu)) { -				listpos = &next->entry; +			if (cputime_gt(next->expires.cpu, nt->expires.cpu))  				break; -			} +			listpos = &next->entry;  		}  	}  	list_add(&nt->entry, listpos); @@ -730,9 +731,15 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  	 * Disarm any old timer after extracting its expiry time.  	 */  	BUG_ON(!irqs_disabled()); + +	ret = 0;  	spin_lock(&p->sighand->siglock);  	old_expires = timer->it.cpu.expires; -	list_del_init(&timer->it.cpu.entry); +	if (unlikely(timer->it.cpu.firing)) { +		timer->it.cpu.firing = -1; +		ret = TIMER_RETRY; +	} else +		list_del_init(&timer->it.cpu.entry);  	spin_unlock(&p->sighand->siglock);  	/* @@ -780,7 +787,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  		}  	} -	if (unlikely(timer->it.cpu.firing)) { +	if (unlikely(ret)) {  		/*  		 * We are colliding with the timer actually firing.  		 * Punt after filling in the timer's old value, and @@ -788,8 +795,6 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  		 * it as an overrun (thanks to bump_cpu_timer above).  		 */  		read_unlock(&tasklist_lock); -		timer->it.cpu.firing = -1; -		ret = TIMER_RETRY;  		goto out;  	} @@ -955,14 +960,16 @@ void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)  static void check_thread_timers(struct task_struct *tsk,  				struct list_head *firing)  { +	int maxfire;  	struct list_head *timers = tsk->cpu_timers; +	maxfire = 20;  	tsk->it_prof_expires = cputime_zero;  	while (!list_empty(timers)) {  		struct cpu_timer_list *t = list_entry(timers->next,  						      struct cpu_timer_list,  						      entry); -		if (cputime_lt(prof_ticks(tsk), t->expires.cpu)) { +		if (!--maxfire || cputime_lt(prof_ticks(tsk), t->expires.cpu)) {  			tsk->it_prof_expires = t->expires.cpu;  			break;  		} @@ -971,12 +978,13 @@ static void check_thread_timers(struct task_struct *tsk,  	}  	++timers; +	maxfire = 20;  	tsk->it_virt_expires = cputime_zero;  	while (!list_empty(timers)) {  		struct cpu_timer_list *t = list_entry(timers->next,  						      struct cpu_timer_list,  						      entry); -		if (cputime_lt(virt_ticks(tsk), t->expires.cpu)) { +		if (!--maxfire || cputime_lt(virt_ticks(tsk), t->expires.cpu)) {  			tsk->it_virt_expires = t->expires.cpu;  			break;  		} @@ -985,12 +993,13 @@ static void check_thread_timers(struct task_struct *tsk,  	}  	++timers; +	maxfire = 20;  	tsk->it_sched_expires = 0;  	while (!list_empty(timers)) {  		struct cpu_timer_list *t = list_entry(timers->next,  						      struct cpu_timer_list,  						      entry); -		if (tsk->sched_time < t->expires.sched) { +		if (!--maxfire || tsk->sched_time < t->expires.sched) {  			tsk->it_sched_expires = t->expires.sched;  			break;  		} @@ -1007,6 +1016,7 @@ static void check_thread_timers(struct task_struct *tsk,  static void check_process_timers(struct task_struct *tsk,  				 struct list_head *firing)  { +	int maxfire;  	struct signal_struct *const sig = tsk->signal;  	cputime_t utime, stime, ptime, virt_expires, prof_expires;  	unsigned long long sched_time, sched_expires; @@ -1039,12 +1049,13 @@ static void check_process_timers(struct task_struct *tsk,  	} while (t != tsk);  	ptime = cputime_add(utime, stime); +	maxfire = 20;  	prof_expires = cputime_zero;  	while (!list_empty(timers)) {  		struct cpu_timer_list *t = list_entry(timers->next,  						      struct cpu_timer_list,  						      entry); -		if (cputime_lt(ptime, t->expires.cpu)) { +		if (!--maxfire || cputime_lt(ptime, t->expires.cpu)) {  			prof_expires = t->expires.cpu;  			break;  		} @@ -1053,12 +1064,13 @@ static void check_process_timers(struct task_struct *tsk,  	}  	++timers; +	maxfire = 20;  	virt_expires = cputime_zero;  	while (!list_empty(timers)) {  		struct cpu_timer_list *t = list_entry(timers->next,  						      struct cpu_timer_list,  						      entry); -		if (cputime_lt(utime, t->expires.cpu)) { +		if (!--maxfire || cputime_lt(utime, t->expires.cpu)) {  			virt_expires = t->expires.cpu;  			break;  		} @@ -1067,12 +1079,13 @@ static void check_process_timers(struct task_struct *tsk,  	}  	++timers; +	maxfire = 20;  	sched_expires = 0;  	while (!list_empty(timers)) {  		struct cpu_timer_list *t = list_entry(timers->next,  						      struct cpu_timer_list,  						      entry); -		if (sched_time < t->expires.sched) { +		if (!--maxfire || sched_time < t->expires.sched) {  			sched_expires = t->expires.sched;  			break;  		} @@ -1155,6 +1168,9 @@ static void check_process_timers(struct task_struct *tsk,  		unsigned long long sched_left, sched;  		const unsigned int nthreads = atomic_read(&sig->live); +		if (!nthreads) +			return; +  		prof_left = cputime_sub(prof_expires, utime);  		prof_left = cputime_sub(prof_left, stime);  		prof_left = cputime_div(prof_left, nthreads); @@ -1191,7 +1207,7 @@ static void check_process_timers(struct task_struct *tsk,  			do {  				t = next_thread(t); -			} while (unlikely(t->exit_state)); +			} while (unlikely(t->flags & PF_EXITING));  		} while (t != tsk);  	}  }  | 
