diff options
Diffstat (limited to 'kernel/time/posix-cpu-timers.c')
| -rw-r--r-- | kernel/time/posix-cpu-timers.c | 90 | 
1 files changed, 73 insertions, 17 deletions
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 517be7fd175e..ee736861b18f 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -291,6 +291,8 @@ static void thread_group_start_cputime(struct task_struct *tsk, u64 *samples)  	struct thread_group_cputimer *cputimer = &tsk->signal->cputimer;  	struct posix_cputimers *pct = &tsk->signal->posix_cputimers; +	lockdep_assert_task_sighand_held(tsk); +  	/* Check if cputimer isn't running. This is accessed without locking. */  	if (!READ_ONCE(pct->timers_active)) {  		struct task_cputime sum; @@ -405,6 +407,55 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer)  	return 0;  } +static struct posix_cputimer_base *timer_base(struct k_itimer *timer, +					      struct task_struct *tsk) +{ +	int clkidx = CPUCLOCK_WHICH(timer->it_clock); + +	if (CPUCLOCK_PERTHREAD(timer->it_clock)) +		return tsk->posix_cputimers.bases + clkidx; +	else +		return tsk->signal->posix_cputimers.bases + clkidx; +} + +/* + * Force recalculating the base earliest expiration on the next tick. + * This will also re-evaluate the need to keep around the process wide + * cputime counter and tick dependency and eventually shut these down + * if necessary. + */ +static void trigger_base_recalc_expires(struct k_itimer *timer, +					struct task_struct *tsk) +{ +	struct posix_cputimer_base *base = timer_base(timer, tsk); + +	base->nextevt = 0; +} + +/* + * Dequeue the timer and reset the base if it was its earliest expiration. + * It makes sure the next tick recalculates the base next expiration so we + * don't keep the costly process wide cputime counter around for a random + * amount of time, along with the tick dependency. + * + * If another timer gets queued between this and the next tick, its + * expiration will update the base next event if necessary on the next + * tick. + */ +static void disarm_timer(struct k_itimer *timer, struct task_struct *p) +{ +	struct cpu_timer *ctmr = &timer->it.cpu; +	struct posix_cputimer_base *base; + +	if (!cpu_timer_dequeue(ctmr)) +		return; + +	base = timer_base(timer, p); +	if (cpu_timer_getexpires(ctmr) == base->nextevt) +		trigger_base_recalc_expires(timer, p); +} + +  /*   * Clean up a CPU-clock timer that is about to be destroyed.   * This is called from timer deletion with the timer already locked. @@ -439,7 +490,7 @@ static int posix_cpu_timer_del(struct k_itimer *timer)  		if (timer->it.cpu.firing)  			ret = TIMER_RETRY;  		else -			cpu_timer_dequeue(ctmr); +			disarm_timer(timer, p);  		unlock_task_sighand(p, &flags);  	} @@ -498,15 +549,9 @@ void posix_cpu_timers_exit_group(struct task_struct *tsk)   */  static void arm_timer(struct k_itimer *timer, struct task_struct *p)  { -	int clkidx = CPUCLOCK_WHICH(timer->it_clock); +	struct posix_cputimer_base *base = timer_base(timer, p);  	struct cpu_timer *ctmr = &timer->it.cpu;  	u64 newexp = cpu_timer_getexpires(ctmr); -	struct posix_cputimer_base *base; - -	if (CPUCLOCK_PERTHREAD(timer->it_clock)) -		base = p->posix_cputimers.bases + clkidx; -	else -		base = p->signal->posix_cputimers.bases + clkidx;  	if (!cpu_timer_enqueue(&base->tqhead, ctmr))  		return; @@ -703,16 +748,29 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,  	timer->it_overrun_last = 0;  	timer->it_overrun = -1; -	if (new_expires != 0 && !(val < new_expires)) { +	if (val >= new_expires) { +		if (new_expires != 0) { +			/* +			 * The designated time already passed, so we notify +			 * immediately, even if the thread never runs to +			 * accumulate more time on this clock. +			 */ +			cpu_timer_fire(timer); +		} +  		/* -		 * The designated time already passed, so we notify -		 * immediately, even if the thread never runs to -		 * accumulate more time on this clock. +		 * Make sure we don't keep around the process wide cputime +		 * counter or the tick dependency if they are not necessary.  		 */ -		cpu_timer_fire(timer); -	} +		sighand = lock_task_sighand(p, &flags); +		if (!sighand) +			goto out; + +		if (!cpu_timer_queued(ctmr)) +			trigger_base_recalc_expires(timer, p); -	ret = 0; +		unlock_task_sighand(p, &flags); +	}   out:  	rcu_read_unlock();  	if (old) @@ -1346,8 +1404,6 @@ void set_process_cpu_timer(struct task_struct *tsk, unsigned int clkid,  			}  		} -		if (!*newval) -			return;  		*newval += now;  	}  | 
