diff options
| author | Grant Likely <grant.likely@secretlab.ca> | 2010-05-25 00:38:26 -0600 | 
|---|---|---|
| committer | Grant Likely <grant.likely@secretlab.ca> | 2010-05-25 00:38:26 -0600 | 
| commit | b1e50ebcf24668e57f058deb48b0704b5391ed0f (patch) | |
| tree | 17e1b69b249d0738317b732186340c9dd053f1a1 /kernel/posix-cpu-timers.c | |
| parent | 0c2a2ae32793e3500a15a449612485f5d17dd431 (diff) | |
| parent | 7e125f7b9cbfce4101191b8076d606c517a73066 (diff) | |
Merge remote branch 'origin' into secretlab/next-spi
Diffstat (limited to 'kernel/posix-cpu-timers.c')
| -rw-r--r-- | kernel/posix-cpu-timers.c | 298 | 
1 files changed, 103 insertions, 195 deletions
| diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index bc7704b3a443..00bb252f29a2 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -11,19 +11,18 @@  #include <trace/events/timer.h>  /* - * Called after updating RLIMIT_CPU to set timer expiration if necessary. + * Called after updating RLIMIT_CPU to run cpu timer and update + * tsk->signal->cputime_expires expiration cache if necessary. Needs + * siglock protection since other code may update expiration cache as + * well.   */  void update_rlimit_cpu(unsigned long rlim_new)  {  	cputime_t cputime = secs_to_cputime(rlim_new); -	struct signal_struct *const sig = current->signal; -	if (cputime_eq(sig->it[CPUCLOCK_PROF].expires, cputime_zero) || -	    cputime_gt(sig->it[CPUCLOCK_PROF].expires, cputime)) { -		spin_lock_irq(¤t->sighand->siglock); -		set_process_cpu_timer(current, CPUCLOCK_PROF, &cputime, NULL); -		spin_unlock_irq(¤t->sighand->siglock); -	} +	spin_lock_irq(¤t->sighand->siglock); +	set_process_cpu_timer(current, CPUCLOCK_PROF, &cputime, NULL); +	spin_unlock_irq(¤t->sighand->siglock);  }  static int check_clock(const clockid_t which_clock) @@ -548,111 +547,62 @@ static inline int expires_gt(cputime_t expires, cputime_t new_exp)  	       cputime_gt(expires, new_exp);  } -static inline int expires_le(cputime_t expires, cputime_t new_exp) -{ -	return !cputime_eq(expires, cputime_zero) && -	       cputime_le(expires, new_exp); -}  /*   * Insert the timer on the appropriate list before any timers that   * expire later.  This must be called with the tasklist_lock held - * for reading, and interrupts disabled. + * for reading, interrupts disabled and p->sighand->siglock taken.   */ -static void arm_timer(struct k_itimer *timer, union cpu_time_count now) +static void arm_timer(struct k_itimer *timer)  {  	struct task_struct *p = timer->it.cpu.task;  	struct list_head *head, *listpos; +	struct task_cputime *cputime_expires;  	struct cpu_timer_list *const nt = &timer->it.cpu;  	struct cpu_timer_list *next; -	unsigned long i; -	head = (CPUCLOCK_PERTHREAD(timer->it_clock) ? -		p->cpu_timers : p->signal->cpu_timers); +	if (CPUCLOCK_PERTHREAD(timer->it_clock)) { +		head = p->cpu_timers; +		cputime_expires = &p->cputime_expires; +	} else { +		head = p->signal->cpu_timers; +		cputime_expires = &p->signal->cputime_expires; +	}  	head += CPUCLOCK_WHICH(timer->it_clock); -	BUG_ON(!irqs_disabled()); -	spin_lock(&p->sighand->siglock); -  	listpos = head; -	if (CPUCLOCK_WHICH(timer->it_clock) == CPUCLOCK_SCHED) { -		list_for_each_entry(next, head, 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)) -				break; -			listpos = &next->entry; -		} +	list_for_each_entry(next, head, entry) { +		if (cpu_time_before(timer->it_clock, nt->expires, next->expires)) +			break; +		listpos = &next->entry;  	}  	list_add(&nt->entry, listpos);  	if (listpos == head) { +		union cpu_time_count *exp = &nt->expires; +  		/* -		 * We are the new earliest-expiring timer. -		 * If we are a thread timer, there can always -		 * be a process timer telling us to stop earlier. +		 * We are the new earliest-expiring POSIX 1.b timer, hence +		 * need to update expiration cache. Take into account that +		 * for process timers we share expiration cache with itimers +		 * and RLIMIT_CPU and for thread timers with RLIMIT_RTTIME.  		 */ -		if (CPUCLOCK_PERTHREAD(timer->it_clock)) { -			union cpu_time_count *exp = &nt->expires; - -			switch (CPUCLOCK_WHICH(timer->it_clock)) { -			default: -				BUG(); -			case CPUCLOCK_PROF: -				if (expires_gt(p->cputime_expires.prof_exp, -					       exp->cpu)) -					p->cputime_expires.prof_exp = exp->cpu; -				break; -			case CPUCLOCK_VIRT: -				if (expires_gt(p->cputime_expires.virt_exp, -					       exp->cpu)) -					p->cputime_expires.virt_exp = exp->cpu; -				break; -			case CPUCLOCK_SCHED: -				if (p->cputime_expires.sched_exp == 0 || -				    p->cputime_expires.sched_exp > exp->sched) -					p->cputime_expires.sched_exp = -								exp->sched; -				break; -			} -		} else { -			struct signal_struct *const sig = p->signal; -			union cpu_time_count *exp = &timer->it.cpu.expires; - -			/* -			 * For a process timer, set the cached expiration time. -			 */ -			switch (CPUCLOCK_WHICH(timer->it_clock)) { -			default: -				BUG(); -			case CPUCLOCK_VIRT: -				if (expires_le(sig->it[CPUCLOCK_VIRT].expires, -					       exp->cpu)) -					break; -				sig->cputime_expires.virt_exp = exp->cpu; -				break; -			case CPUCLOCK_PROF: -				if (expires_le(sig->it[CPUCLOCK_PROF].expires, -					       exp->cpu)) -					break; -				i = sig->rlim[RLIMIT_CPU].rlim_cur; -				if (i != RLIM_INFINITY && -				    i <= cputime_to_secs(exp->cpu)) -					break; -				sig->cputime_expires.prof_exp = exp->cpu; -				break; -			case CPUCLOCK_SCHED: -				sig->cputime_expires.sched_exp = exp->sched; -				break; -			} +		switch (CPUCLOCK_WHICH(timer->it_clock)) { +		case CPUCLOCK_PROF: +			if (expires_gt(cputime_expires->prof_exp, exp->cpu)) +				cputime_expires->prof_exp = exp->cpu; +			break; +		case CPUCLOCK_VIRT: +			if (expires_gt(cputime_expires->virt_exp, exp->cpu)) +				cputime_expires->virt_exp = exp->cpu; +			break; +		case CPUCLOCK_SCHED: +			if (cputime_expires->sched_exp == 0 || +			    cputime_expires->sched_exp > exp->sched) +				cputime_expires->sched_exp = exp->sched; +			break;  		}  	} - -	spin_unlock(&p->sighand->siglock);  }  /* @@ -660,7 +610,12 @@ static void arm_timer(struct k_itimer *timer, union cpu_time_count now)   */  static void cpu_timer_fire(struct k_itimer *timer)  { -	if (unlikely(timer->sigq == NULL)) { +	if ((timer->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) { +		/* +		 * User don't want any signal. +		 */ +		timer->it.cpu.expires.sched = 0; +	} else if (unlikely(timer->sigq == NULL)) {  		/*  		 * This a special case for clock_nanosleep,  		 * not a normal timer from sys_timer_create. @@ -721,7 +676,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  			struct itimerspec *new, struct itimerspec *old)  {  	struct task_struct *p = timer->it.cpu.task; -	union cpu_time_count old_expires, new_expires, val; +	union cpu_time_count old_expires, new_expires, old_incr, val;  	int ret;  	if (unlikely(p == NULL)) { @@ -752,6 +707,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  	BUG_ON(!irqs_disabled());  	ret = 0; +	old_incr = timer->it.cpu.incr;  	spin_lock(&p->sighand->siglock);  	old_expires = timer->it.cpu.expires;  	if (unlikely(timer->it.cpu.firing)) { @@ -759,7 +715,6 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  		ret = TIMER_RETRY;  	} else  		list_del_init(&timer->it.cpu.entry); -	spin_unlock(&p->sighand->siglock);  	/*  	 * We need to sample the current value to convert the new @@ -813,6 +768,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  		 * disable this firing since we are already reporting  		 * it as an overrun (thanks to bump_cpu_timer above).  		 */ +		spin_unlock(&p->sighand->siglock);  		read_unlock(&tasklist_lock);  		goto out;  	} @@ -828,11 +784,11 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  	 */  	timer->it.cpu.expires = new_expires;  	if (new_expires.sched != 0 && -	    (timer->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE &&  	    cpu_time_before(timer->it_clock, val, new_expires)) { -		arm_timer(timer, val); +		arm_timer(timer);  	} +	spin_unlock(&p->sighand->siglock);  	read_unlock(&tasklist_lock);  	/* @@ -853,7 +809,6 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,  	timer->it_overrun = -1;  	if (new_expires.sched != 0 && -	    (timer->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE &&  	    !cpu_time_before(timer->it_clock, val, new_expires)) {  		/*  		 * The designated time already passed, so we notify @@ -867,7 +822,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags,   out:  	if (old) {  		sample_to_timespec(timer->it_clock, -				   timer->it.cpu.incr, &old->it_interval); +				   old_incr, &old->it_interval);  	}  	return ret;  } @@ -927,25 +882,6 @@ void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)  		read_unlock(&tasklist_lock);  	} -	if ((timer->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) { -		if (timer->it.cpu.incr.sched == 0 && -		    cpu_time_before(timer->it_clock, -				    timer->it.cpu.expires, now)) { -			/* -			 * Do-nothing timer expired and has no reload, -			 * so it's as if it was never set. -			 */ -			timer->it.cpu.expires.sched = 0; -			itp->it_value.tv_sec = itp->it_value.tv_nsec = 0; -			return; -		} -		/* -		 * Account for any expirations and reloads that should -		 * have happened. -		 */ -		bump_cpu_timer(timer, now); -	} -  	if (unlikely(clear_dead)) {  		/*  		 * We've noticed that the thread is dead, but @@ -1066,16 +1002,9 @@ static void stop_process_timers(struct signal_struct *sig)  	struct thread_group_cputimer *cputimer = &sig->cputimer;  	unsigned long flags; -	if (!cputimer->running) -		return; -  	spin_lock_irqsave(&cputimer->lock, flags);  	cputimer->running = 0;  	spin_unlock_irqrestore(&cputimer->lock, flags); - -	sig->cputime_expires.prof_exp = cputime_zero; -	sig->cputime_expires.virt_exp = cputime_zero; -	sig->cputime_expires.sched_exp = 0;  }  static u32 onecputick; @@ -1112,6 +1041,23 @@ static void check_cpu_itimer(struct task_struct *tsk, struct cpu_itimer *it,  	}  } +/** + * task_cputime_zero - Check a task_cputime struct for all zero fields. + * + * @cputime:	The struct to compare. + * + * Checks @cputime to see if all fields are zero.  Returns true if all fields + * are zero, false if any field is nonzero. + */ +static inline int task_cputime_zero(const struct task_cputime *cputime) +{ +	if (cputime_eq(cputime->utime, cputime_zero) && +	    cputime_eq(cputime->stime, cputime_zero) && +	    cputime->sum_exec_runtime == 0) +		return 1; +	return 0; +} +  /*   * Check for any per-thread CPU timers that have fired and move them   * off the tsk->*_timers list onto the firing list.  Per-thread timers @@ -1129,19 +1075,6 @@ static void check_process_timers(struct task_struct *tsk,  	unsigned long soft;  	/* -	 * Don't sample the current process CPU clocks if there are no timers. -	 */ -	if (list_empty(&timers[CPUCLOCK_PROF]) && -	    cputime_eq(sig->it[CPUCLOCK_PROF].expires, cputime_zero) && -	    sig->rlim[RLIMIT_CPU].rlim_cur == RLIM_INFINITY && -	    list_empty(&timers[CPUCLOCK_VIRT]) && -	    cputime_eq(sig->it[CPUCLOCK_VIRT].expires, cputime_zero) && -	    list_empty(&timers[CPUCLOCK_SCHED])) { -		stop_process_timers(sig); -		return; -	} - -	/*  	 * Collect the current process totals.  	 */  	thread_group_cputimer(tsk, &cputime); @@ -1230,18 +1163,11 @@ static void check_process_timers(struct task_struct *tsk,  		}  	} -	if (!cputime_eq(prof_expires, cputime_zero) && -	    (cputime_eq(sig->cputime_expires.prof_exp, cputime_zero) || -	     cputime_gt(sig->cputime_expires.prof_exp, prof_expires))) -		sig->cputime_expires.prof_exp = prof_expires; -	if (!cputime_eq(virt_expires, cputime_zero) && -	    (cputime_eq(sig->cputime_expires.virt_exp, cputime_zero) || -	     cputime_gt(sig->cputime_expires.virt_exp, virt_expires))) -		sig->cputime_expires.virt_exp = virt_expires; -	if (sched_expires != 0 && -	    (sig->cputime_expires.sched_exp == 0 || -	     sig->cputime_expires.sched_exp > sched_expires)) -		sig->cputime_expires.sched_exp = sched_expires; +	sig->cputime_expires.prof_exp = prof_expires; +	sig->cputime_expires.virt_exp = virt_expires; +	sig->cputime_expires.sched_exp = sched_expires; +	if (task_cputime_zero(&sig->cputime_expires)) +		stop_process_timers(sig);  }  /* @@ -1270,6 +1196,7 @@ void posix_cpu_timer_schedule(struct k_itimer *timer)  			goto out;  		}  		read_lock(&tasklist_lock); /* arm_timer needs it.  */ +		spin_lock(&p->sighand->siglock);  	} else {  		read_lock(&tasklist_lock);  		if (unlikely(p->signal == NULL)) { @@ -1290,6 +1217,7 @@ void posix_cpu_timer_schedule(struct k_itimer *timer)  			clear_dead_task(timer, now);  			goto out_unlock;  		} +		spin_lock(&p->sighand->siglock);  		cpu_timer_sample_group(timer->it_clock, p, &now);  		bump_cpu_timer(timer, now);  		/* Leave the tasklist_lock locked for the call below.  */ @@ -1298,7 +1226,9 @@ void posix_cpu_timer_schedule(struct k_itimer *timer)  	/*  	 * Now re-arm for the new expiry time.  	 */ -	arm_timer(timer, now); +	BUG_ON(!irqs_disabled()); +	arm_timer(timer); +	spin_unlock(&p->sighand->siglock);  out_unlock:  	read_unlock(&tasklist_lock); @@ -1310,23 +1240,6 @@ out:  }  /** - * task_cputime_zero - Check a task_cputime struct for all zero fields. - * - * @cputime:	The struct to compare. - * - * Checks @cputime to see if all fields are zero.  Returns true if all fields - * are zero, false if any field is nonzero. - */ -static inline int task_cputime_zero(const struct task_cputime *cputime) -{ -	if (cputime_eq(cputime->utime, cputime_zero) && -	    cputime_eq(cputime->stime, cputime_zero) && -	    cputime->sum_exec_runtime == 0) -		return 1; -	return 0; -} - -/**   * task_cputime_expired - Compare two task_cputime entities.   *   * @sample:	The task_cputime structure to be checked for expiration. @@ -1382,7 +1295,7 @@ static inline int fastpath_timer_check(struct task_struct *tsk)  	}  	sig = tsk->signal; -	if (!task_cputime_zero(&sig->cputime_expires)) { +	if (sig->cputimer.running) {  		struct task_cputime group_sample;  		thread_group_cputimer(tsk, &group_sample); @@ -1390,7 +1303,7 @@ static inline int fastpath_timer_check(struct task_struct *tsk)  			return 1;  	} -	return sig->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY; +	return 0;  }  /* @@ -1419,7 +1332,12 @@ void run_posix_cpu_timers(struct task_struct *tsk)  	 * put them on the firing list.  	 */  	check_thread_timers(tsk, &firing); -	check_process_timers(tsk, &firing); +	/* +	 * If there are any active process wide timers (POSIX 1.b, itimers, +	 * RLIMIT_CPU) cputimer must be running. +	 */ +	if (tsk->signal->cputimer.running) +		check_process_timers(tsk, &firing);  	/*  	 * We must release these locks before taking any timer's lock. @@ -1456,21 +1374,23 @@ void run_posix_cpu_timers(struct task_struct *tsk)  }  /* - * Set one of the process-wide special case CPU timers. + * Set one of the process-wide special case CPU timers or RLIMIT_CPU.   * The tsk->sighand->siglock must be held by the caller. - * The *newval argument is relative and we update it to be absolute, *oldval - * is absolute and we update it to be relative.   */  void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx,  			   cputime_t *newval, cputime_t *oldval)  {  	union cpu_time_count now; -	struct list_head *head;  	BUG_ON(clock_idx == CPUCLOCK_SCHED);  	cpu_timer_sample_group(clock_idx, tsk, &now);  	if (oldval) { +		/* +		 * We are setting itimer. The *oldval is absolute and we update +		 * it to be relative, *newval argument is relative and we update +		 * it to be absolute. +		 */  		if (!cputime_eq(*oldval, cputime_zero)) {  			if (cputime_le(*oldval, now.cpu)) {  				/* Just about to fire. */ @@ -1483,33 +1403,21 @@ void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx,  		if (cputime_eq(*newval, cputime_zero))  			return;  		*newval = cputime_add(*newval, now.cpu); - -		/* -		 * If the RLIMIT_CPU timer will expire before the -		 * ITIMER_PROF timer, we have nothing else to do. -		 */ -		if (tsk->signal->rlim[RLIMIT_CPU].rlim_cur -		    < cputime_to_secs(*newval)) -			return;  	}  	/* -	 * Check whether there are any process timers already set to fire -	 * before this one.  If so, we don't have anything more to do. +	 * Update expiration cache if we are the earliest timer, or eventually +	 * RLIMIT_CPU limit is earlier than prof_exp cpu timer expire.  	 */ -	head = &tsk->signal->cpu_timers[clock_idx]; -	if (list_empty(head) || -	    cputime_ge(list_first_entry(head, -				  struct cpu_timer_list, entry)->expires.cpu, -		       *newval)) { -		switch (clock_idx) { -		case CPUCLOCK_PROF: +	switch (clock_idx) { +	case CPUCLOCK_PROF: +		if (expires_gt(tsk->signal->cputime_expires.prof_exp, *newval))  			tsk->signal->cputime_expires.prof_exp = *newval; -			break; -		case CPUCLOCK_VIRT: +		break; +	case CPUCLOCK_VIRT: +		if (expires_gt(tsk->signal->cputime_expires.virt_exp, *newval))  			tsk->signal->cputime_expires.virt_exp = *newval; -			break; -		} +		break;  	}  } | 
