forked from mirrors/linux
		
	posix-cpu-timers: Utilize timerqueue for storage
Using a linear O(N) search for timer insertion affects execution time and D-cache footprint badly with a larger number of timers. Switch the storage to a timerqueue which is already used for hrtimers and alarmtimers. It does not affect the size of struct k_itimer as it.alarm is still larger. The extra list head for the expiry list will go away later once the expiry is moved into task work context. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Frederic Weisbecker <frederic@kernel.org> Link: https://lkml.kernel.org/r/alpine.DEB.2.21.1908272129220.1939@nanos.tec.linutronix.de
This commit is contained in:
		
							parent
							
								
									244d49e306
								
							
						
					
					
						commit
						60bda037f1
					
				
					 3 changed files with 152 additions and 106 deletions
				
			
		|  | @ -5,17 +5,11 @@ | ||||||
| #include <linux/spinlock.h> | #include <linux/spinlock.h> | ||||||
| #include <linux/list.h> | #include <linux/list.h> | ||||||
| #include <linux/alarmtimer.h> | #include <linux/alarmtimer.h> | ||||||
|  | #include <linux/timerqueue.h> | ||||||
| 
 | 
 | ||||||
| struct kernel_siginfo; | struct kernel_siginfo; | ||||||
| struct task_struct; | struct task_struct; | ||||||
| 
 | 
 | ||||||
| struct cpu_timer_list { |  | ||||||
| 	struct list_head entry; |  | ||||||
| 	u64 expires; |  | ||||||
| 	struct task_struct *task; |  | ||||||
| 	int firing; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /*
 | /*
 | ||||||
|  * Bit fields within a clockid: |  * Bit fields within a clockid: | ||||||
|  * |  * | ||||||
|  | @ -64,14 +58,58 @@ static inline int clockid_to_fd(const clockid_t clk) | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_POSIX_TIMERS | #ifdef CONFIG_POSIX_TIMERS | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * cpu_timer - Posix CPU timer representation for k_itimer | ||||||
|  |  * @node:	timerqueue node to queue in the task/sig | ||||||
|  |  * @head:	timerqueue head on which this timer is queued | ||||||
|  |  * @task:	Pointer to target task | ||||||
|  |  * @elist:	List head for the expiry list | ||||||
|  |  * @firing:	Timer is currently firing | ||||||
|  |  */ | ||||||
|  | struct cpu_timer { | ||||||
|  | 	struct timerqueue_node	node; | ||||||
|  | 	struct timerqueue_head	*head; | ||||||
|  | 	struct task_struct	*task; | ||||||
|  | 	struct list_head	elist; | ||||||
|  | 	int			firing; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline bool cpu_timer_requeue(struct cpu_timer *ctmr) | ||||||
|  | { | ||||||
|  | 	return timerqueue_add(ctmr->head, &ctmr->node); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool cpu_timer_enqueue(struct timerqueue_head *head, | ||||||
|  | 				     struct cpu_timer *ctmr) | ||||||
|  | { | ||||||
|  | 	ctmr->head = head; | ||||||
|  | 	return timerqueue_add(head, &ctmr->node); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void cpu_timer_dequeue(struct cpu_timer *ctmr) | ||||||
|  | { | ||||||
|  | 	if (!RB_EMPTY_NODE(&ctmr->node.node)) | ||||||
|  | 		timerqueue_del(ctmr->head, &ctmr->node); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u64 cpu_timer_getexpires(struct cpu_timer *ctmr) | ||||||
|  | { | ||||||
|  | 	return ctmr->node.expires; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void cpu_timer_setexpires(struct cpu_timer *ctmr, u64 exp) | ||||||
|  | { | ||||||
|  | 	ctmr->node.expires = exp; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * posix_cputimer_base - Container per posix CPU clock |  * posix_cputimer_base - Container per posix CPU clock | ||||||
|  * @nextevt:		Earliest-expiration cache |  * @nextevt:		Earliest-expiration cache | ||||||
|  * @cpu_timers:		List heads to queue posix CPU timers |  * @tqhead:		timerqueue head for cpu_timers | ||||||
|  */ |  */ | ||||||
| struct posix_cputimer_base { | struct posix_cputimer_base { | ||||||
| 	u64			nextevt; | 	u64			nextevt; | ||||||
| 	struct list_head	cpu_timers; | 	struct timerqueue_head	tqhead; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -92,14 +130,10 @@ struct posix_cputimers { | ||||||
| 
 | 
 | ||||||
| static inline void posix_cputimers_init(struct posix_cputimers *pct) | static inline void posix_cputimers_init(struct posix_cputimers *pct) | ||||||
| { | { | ||||||
| 	pct->timers_active = 0; | 	memset(pct, 0, sizeof(*pct)); | ||||||
| 	pct->expiry_active = 0; |  | ||||||
| 	pct->bases[0].nextevt = U64_MAX; | 	pct->bases[0].nextevt = U64_MAX; | ||||||
| 	pct->bases[1].nextevt = U64_MAX; | 	pct->bases[1].nextevt = U64_MAX; | ||||||
| 	pct->bases[2].nextevt = U64_MAX; | 	pct->bases[2].nextevt = U64_MAX; | ||||||
| 	INIT_LIST_HEAD(&pct->bases[0].cpu_timers); |  | ||||||
| 	INIT_LIST_HEAD(&pct->bases[1].cpu_timers); |  | ||||||
| 	INIT_LIST_HEAD(&pct->bases[2].cpu_timers); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit); | void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit); | ||||||
|  | @ -113,7 +147,6 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct, | ||||||
| /* Init task static initializer */ | /* Init task static initializer */ | ||||||
| #define INIT_CPU_TIMERBASE(b) {						\ | #define INIT_CPU_TIMERBASE(b) {						\ | ||||||
| 	.nextevt	= U64_MAX,					\ | 	.nextevt	= U64_MAX,					\ | ||||||
| 	.cpu_timers	= LIST_HEAD_INIT(b.cpu_timers),			\ |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define INIT_CPU_TIMERBASES(b) {					\ | #define INIT_CPU_TIMERBASES(b) {					\ | ||||||
|  | @ -182,7 +215,7 @@ struct k_itimer { | ||||||
| 		struct { | 		struct { | ||||||
| 			struct hrtimer	timer; | 			struct hrtimer	timer; | ||||||
| 		} real; | 		} real; | ||||||
| 		struct cpu_timer_list	cpu; | 		struct cpu_timer	cpu; | ||||||
| 		struct { | 		struct { | ||||||
| 			struct alarm	alarmtimer; | 			struct alarm	alarmtimer; | ||||||
| 		} alarm; | 		} alarm; | ||||||
|  |  | ||||||
|  | @ -43,6 +43,16 @@ static inline void timerqueue_init(struct timerqueue_node *node) | ||||||
| 	RB_CLEAR_NODE(&node->node); | 	RB_CLEAR_NODE(&node->node); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline bool timerqueue_node_queued(struct timerqueue_node *node) | ||||||
|  | { | ||||||
|  | 	return !RB_EMPTY_NODE(&node->node); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool timerqueue_node_expires(struct timerqueue_node *node) | ||||||
|  | { | ||||||
|  | 	return node->expires; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline void timerqueue_init_head(struct timerqueue_head *head) | static inline void timerqueue_init_head(struct timerqueue_head *head) | ||||||
| { | { | ||||||
| 	head->rb_root = RB_ROOT_CACHED; | 	head->rb_root = RB_ROOT_CACHED; | ||||||
|  |  | ||||||
|  | @ -96,19 +96,19 @@ static inline int validate_clock_permissions(const clockid_t clock) | ||||||
|  * Update expiry time from increment, and increase overrun count, |  * Update expiry time from increment, and increase overrun count, | ||||||
|  * given the current clock sample. |  * given the current clock sample. | ||||||
|  */ |  */ | ||||||
| static void bump_cpu_timer(struct k_itimer *timer, u64 now) | static u64 bump_cpu_timer(struct k_itimer *timer, u64 now) | ||||||
| { | { | ||||||
|  | 	u64 delta, incr, expires = timer->it.cpu.node.expires; | ||||||
| 	int i; | 	int i; | ||||||
| 	u64 delta, incr; |  | ||||||
| 
 | 
 | ||||||
| 	if (!timer->it_interval) | 	if (!timer->it_interval) | ||||||
| 		return; | 		return expires; | ||||||
| 
 | 
 | ||||||
| 	if (now < timer->it.cpu.expires) | 	if (now < expires) | ||||||
| 		return; | 		return expires; | ||||||
| 
 | 
 | ||||||
| 	incr = timer->it_interval; | 	incr = timer->it_interval; | ||||||
| 	delta = now + incr - timer->it.cpu.expires; | 	delta = now + incr - expires; | ||||||
| 
 | 
 | ||||||
| 	/* Don't use (incr*2 < delta), incr*2 might overflow. */ | 	/* Don't use (incr*2 < delta), incr*2 might overflow. */ | ||||||
| 	for (i = 0; incr < delta - incr; i++) | 	for (i = 0; incr < delta - incr; i++) | ||||||
|  | @ -118,10 +118,11 @@ static void bump_cpu_timer(struct k_itimer *timer, u64 now) | ||||||
| 		if (delta < incr) | 		if (delta < incr) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		timer->it.cpu.expires += incr; | 		timer->it.cpu.node.expires += incr; | ||||||
| 		timer->it_overrun += 1LL << i; | 		timer->it_overrun += 1LL << i; | ||||||
| 		delta -= incr; | 		delta -= incr; | ||||||
| 	} | 	} | ||||||
|  | 	return timer->it.cpu.node.expires; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Check whether all cache entries contain U64_MAX, i.e. eternal expiry time */ | /* Check whether all cache entries contain U64_MAX, i.e. eternal expiry time */ | ||||||
|  | @ -365,7 +366,7 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	new_timer->kclock = &clock_posix_cpu; | 	new_timer->kclock = &clock_posix_cpu; | ||||||
| 	INIT_LIST_HEAD(&new_timer->it.cpu.entry); | 	timerqueue_init(&new_timer->it.cpu.node); | ||||||
| 	new_timer->it.cpu.task = p; | 	new_timer->it.cpu.task = p; | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -378,10 +379,11 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer) | ||||||
|  */ |  */ | ||||||
| static int posix_cpu_timer_del(struct k_itimer *timer) | static int posix_cpu_timer_del(struct k_itimer *timer) | ||||||
| { | { | ||||||
| 	int ret = 0; | 	struct cpu_timer *ctmr = &timer->it.cpu; | ||||||
| 	unsigned long flags; | 	struct task_struct *p = ctmr->task; | ||||||
| 	struct sighand_struct *sighand; | 	struct sighand_struct *sighand; | ||||||
| 	struct task_struct *p = timer->it.cpu.task; | 	unsigned long flags; | ||||||
|  | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	if (WARN_ON_ONCE(!p)) | 	if (WARN_ON_ONCE(!p)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | @ -393,15 +395,15 @@ static int posix_cpu_timer_del(struct k_itimer *timer) | ||||||
| 	sighand = lock_task_sighand(p, &flags); | 	sighand = lock_task_sighand(p, &flags); | ||||||
| 	if (unlikely(sighand == NULL)) { | 	if (unlikely(sighand == NULL)) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * We raced with the reaping of the task. | 		 * This raced with the reaping of the task. The exit cleanup | ||||||
| 		 * The deletion should have cleared us off the list. | 		 * should have removed this timer from the timer queue. | ||||||
| 		 */ | 		 */ | ||||||
| 		WARN_ON_ONCE(!list_empty(&timer->it.cpu.entry)); | 		WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); | ||||||
| 	} else { | 	} else { | ||||||
| 		if (timer->it.cpu.firing) | 		if (timer->it.cpu.firing) | ||||||
| 			ret = TIMER_RETRY; | 			ret = TIMER_RETRY; | ||||||
| 		else | 		else | ||||||
| 			list_del(&timer->it.cpu.entry); | 			cpu_timer_dequeue(ctmr); | ||||||
| 
 | 
 | ||||||
| 		unlock_task_sighand(p, &flags); | 		unlock_task_sighand(p, &flags); | ||||||
| 	} | 	} | ||||||
|  | @ -412,12 +414,16 @@ static int posix_cpu_timer_del(struct k_itimer *timer) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void cleanup_timers_list(struct list_head *head) | static void cleanup_timerqueue(struct timerqueue_head *head) | ||||||
| { | { | ||||||
| 	struct cpu_timer_list *timer, *next; | 	struct timerqueue_node *node; | ||||||
|  | 	struct cpu_timer *ctmr; | ||||||
| 
 | 
 | ||||||
| 	list_for_each_entry_safe(timer, next, head, entry) | 	while ((node = timerqueue_getnext(head))) { | ||||||
| 		list_del_init(&timer->entry); | 		timerqueue_del(head, node); | ||||||
|  | 		ctmr = container_of(node, struct cpu_timer, node); | ||||||
|  | 		ctmr->head = NULL; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -429,9 +435,9 @@ static void cleanup_timers_list(struct list_head *head) | ||||||
|  */ |  */ | ||||||
| static void cleanup_timers(struct posix_cputimers *pct) | static void cleanup_timers(struct posix_cputimers *pct) | ||||||
| { | { | ||||||
| 	cleanup_timers_list(&pct->bases[CPUCLOCK_PROF].cpu_timers); | 	cleanup_timerqueue(&pct->bases[CPUCLOCK_PROF].tqhead); | ||||||
| 	cleanup_timers_list(&pct->bases[CPUCLOCK_VIRT].cpu_timers); | 	cleanup_timerqueue(&pct->bases[CPUCLOCK_VIRT].tqhead); | ||||||
| 	cleanup_timers_list(&pct->bases[CPUCLOCK_SCHED].cpu_timers); | 	cleanup_timerqueue(&pct->bases[CPUCLOCK_SCHED].tqhead); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -454,28 +460,18 @@ void posix_cpu_timers_exit_group(struct task_struct *tsk) | ||||||
|  */ |  */ | ||||||
| static void arm_timer(struct k_itimer *timer) | static void arm_timer(struct k_itimer *timer) | ||||||
| { | { | ||||||
| 	struct cpu_timer_list *const nt = &timer->it.cpu; |  | ||||||
| 	int clkidx = CPUCLOCK_WHICH(timer->it_clock); | 	int clkidx = CPUCLOCK_WHICH(timer->it_clock); | ||||||
| 	struct task_struct *p = timer->it.cpu.task; | 	struct cpu_timer *ctmr = &timer->it.cpu; | ||||||
| 	u64 newexp = timer->it.cpu.expires; | 	u64 newexp = cpu_timer_getexpires(ctmr); | ||||||
|  | 	struct task_struct *p = ctmr->task; | ||||||
| 	struct posix_cputimer_base *base; | 	struct posix_cputimer_base *base; | ||||||
| 	struct list_head *head, *listpos; |  | ||||||
| 	struct cpu_timer_list *next; |  | ||||||
| 
 | 
 | ||||||
| 	if (CPUCLOCK_PERTHREAD(timer->it_clock)) | 	if (CPUCLOCK_PERTHREAD(timer->it_clock)) | ||||||
| 		base = p->posix_cputimers.bases + clkidx; | 		base = p->posix_cputimers.bases + clkidx; | ||||||
| 	else | 	else | ||||||
| 		base = p->signal->posix_cputimers.bases + clkidx; | 		base = p->signal->posix_cputimers.bases + clkidx; | ||||||
| 
 | 
 | ||||||
| 	listpos = head = &base->cpu_timers; | 	if (!cpu_timer_enqueue(&base->tqhead, ctmr)) | ||||||
| 	list_for_each_entry(next,head, entry) { |  | ||||||
| 		if (nt->expires < next->expires) |  | ||||||
| 			break; |  | ||||||
| 		listpos = &next->entry; |  | ||||||
| 	} |  | ||||||
| 	list_add(&nt->entry, listpos); |  | ||||||
| 
 |  | ||||||
| 	if (listpos != head) |  | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
|  | @ -498,24 +494,26 @@ static void arm_timer(struct k_itimer *timer) | ||||||
|  */ |  */ | ||||||
| static void cpu_timer_fire(struct k_itimer *timer) | static void cpu_timer_fire(struct k_itimer *timer) | ||||||
| { | { | ||||||
|  | 	struct cpu_timer *ctmr = &timer->it.cpu; | ||||||
|  | 
 | ||||||
| 	if ((timer->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) { | 	if ((timer->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * User don't want any signal. | 		 * User don't want any signal. | ||||||
| 		 */ | 		 */ | ||||||
| 		timer->it.cpu.expires = 0; | 		cpu_timer_setexpires(ctmr, 0); | ||||||
| 	} else if (unlikely(timer->sigq == NULL)) { | 	} else if (unlikely(timer->sigq == NULL)) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * This a special case for clock_nanosleep, | 		 * This a special case for clock_nanosleep, | ||||||
| 		 * not a normal timer from sys_timer_create. | 		 * not a normal timer from sys_timer_create. | ||||||
| 		 */ | 		 */ | ||||||
| 		wake_up_process(timer->it_process); | 		wake_up_process(timer->it_process); | ||||||
| 		timer->it.cpu.expires = 0; | 		cpu_timer_setexpires(ctmr, 0); | ||||||
| 	} else if (!timer->it_interval) { | 	} else if (!timer->it_interval) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * One-shot timer.  Clear it as soon as it's fired. | 		 * One-shot timer.  Clear it as soon as it's fired. | ||||||
| 		 */ | 		 */ | ||||||
| 		posix_timer_event(timer, 0); | 		posix_timer_event(timer, 0); | ||||||
| 		timer->it.cpu.expires = 0; | 		cpu_timer_setexpires(ctmr, 0); | ||||||
| 	} else if (posix_timer_event(timer, ++timer->it_requeue_pending)) { | 	} else if (posix_timer_event(timer, ++timer->it_requeue_pending)) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * The signal did not get queued because the signal | 		 * The signal did not get queued because the signal | ||||||
|  | @ -539,10 +537,11 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, | ||||||
| { | { | ||||||
| 	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); | 	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); | ||||||
| 	u64 old_expires, new_expires, old_incr, val; | 	u64 old_expires, new_expires, old_incr, val; | ||||||
| 	struct task_struct *p = timer->it.cpu.task; | 	struct cpu_timer *ctmr = &timer->it.cpu; | ||||||
|  | 	struct task_struct *p = ctmr->task; | ||||||
| 	struct sighand_struct *sighand; | 	struct sighand_struct *sighand; | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
| 	int ret; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	if (WARN_ON_ONCE(!p)) | 	if (WARN_ON_ONCE(!p)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | @ -562,22 +561,21 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, | ||||||
| 	 * If p has just been reaped, we can no | 	 * If p has just been reaped, we can no | ||||||
| 	 * longer get any information about it at all. | 	 * longer get any information about it at all. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (unlikely(sighand == NULL)) { | 	if (unlikely(sighand == NULL)) | ||||||
| 		return -ESRCH; | 		return -ESRCH; | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Disarm any old timer after extracting its expiry time. | 	 * Disarm any old timer after extracting its expiry time. | ||||||
| 	 */ | 	 */ | ||||||
| 
 |  | ||||||
| 	ret = 0; |  | ||||||
| 	old_incr = timer->it_interval; | 	old_incr = timer->it_interval; | ||||||
| 	old_expires = timer->it.cpu.expires; | 	old_expires = cpu_timer_getexpires(ctmr); | ||||||
|  | 
 | ||||||
| 	if (unlikely(timer->it.cpu.firing)) { | 	if (unlikely(timer->it.cpu.firing)) { | ||||||
| 		timer->it.cpu.firing = -1; | 		timer->it.cpu.firing = -1; | ||||||
| 		ret = TIMER_RETRY; | 		ret = TIMER_RETRY; | ||||||
| 	} else | 	} else { | ||||||
| 		list_del_init(&timer->it.cpu.entry); | 		cpu_timer_dequeue(ctmr); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * We need to sample the current value to convert the new | 	 * We need to sample the current value to convert the new | ||||||
|  | @ -598,18 +596,16 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, | ||||||
| 			old->it_value.tv_nsec = 0; | 			old->it_value.tv_nsec = 0; | ||||||
| 		} else { | 		} else { | ||||||
| 			/*
 | 			/*
 | ||||||
| 			 * Update the timer in case it has | 			 * Update the timer in case it has overrun already. | ||||||
| 			 * overrun already.  If it has, | 			 * If it has, we'll report it as having overrun and | ||||||
| 			 * we'll report it as having overrun | 			 * with the next reloaded timer already ticking, | ||||||
| 			 * and with the next reloaded timer | 			 * though we are swallowing that pending | ||||||
| 			 * already ticking, though we are | 			 * notification here to install the new setting. | ||||||
| 			 * swallowing that pending |  | ||||||
| 			 * notification here to install the |  | ||||||
| 			 * new setting. |  | ||||||
| 			 */ | 			 */ | ||||||
| 			bump_cpu_timer(timer, val); | 			u64 exp = bump_cpu_timer(timer, val); | ||||||
| 			if (val < timer->it.cpu.expires) { | 
 | ||||||
| 				old_expires = timer->it.cpu.expires - val; | 			if (val < exp) { | ||||||
|  | 				old_expires = exp - val; | ||||||
| 				old->it_value = ns_to_timespec64(old_expires); | 				old->it_value = ns_to_timespec64(old_expires); | ||||||
| 			} else { | 			} else { | ||||||
| 				old->it_value.tv_nsec = 1; | 				old->it_value.tv_nsec = 1; | ||||||
|  | @ -638,7 +634,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, | ||||||
| 	 * For a timer with no notification action, we don't actually | 	 * For a timer with no notification action, we don't actually | ||||||
| 	 * arm the timer (we'll just fake it for timer_gettime). | 	 * arm the timer (we'll just fake it for timer_gettime). | ||||||
| 	 */ | 	 */ | ||||||
| 	timer->it.cpu.expires = new_expires; | 	cpu_timer_setexpires(ctmr, new_expires); | ||||||
| 	if (new_expires != 0 && val < new_expires) { | 	if (new_expires != 0 && val < new_expires) { | ||||||
| 		arm_timer(timer); | 		arm_timer(timer); | ||||||
| 	} | 	} | ||||||
|  | @ -680,8 +676,9 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, | ||||||
| static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp) | static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp) | ||||||
| { | { | ||||||
| 	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); | 	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); | ||||||
| 	struct task_struct *p = timer->it.cpu.task; | 	struct cpu_timer *ctmr = &timer->it.cpu; | ||||||
| 	u64 now; | 	u64 now, expires = cpu_timer_getexpires(ctmr); | ||||||
|  | 	struct task_struct *p = ctmr->task; | ||||||
| 
 | 
 | ||||||
| 	if (WARN_ON_ONCE(!p)) | 	if (WARN_ON_ONCE(!p)) | ||||||
| 		return; | 		return; | ||||||
|  | @ -691,7 +688,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp | ||||||
| 	 */ | 	 */ | ||||||
| 	itp->it_interval = ktime_to_timespec64(timer->it_interval); | 	itp->it_interval = ktime_to_timespec64(timer->it_interval); | ||||||
| 
 | 
 | ||||||
| 	if (!timer->it.cpu.expires) | 	if (!expires) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
|  | @ -713,9 +710,9 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp | ||||||
| 			/*
 | 			/*
 | ||||||
| 			 * The process has been reaped. | 			 * The process has been reaped. | ||||||
| 			 * We can't even collect a sample any more. | 			 * We can't even collect a sample any more. | ||||||
| 			 * Call the timer disarmed, nothing else to do. | 			 * Disarm the timer, nothing else to do. | ||||||
| 			 */ | 			 */ | ||||||
| 			timer->it.cpu.expires = 0; | 			cpu_timer_setexpires(ctmr, 0); | ||||||
| 			return; | 			return; | ||||||
| 		} else { | 		} else { | ||||||
| 			now = cpu_clock_sample_group(clkid, p, false); | 			now = cpu_clock_sample_group(clkid, p, false); | ||||||
|  | @ -723,8 +720,8 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (now < timer->it.cpu.expires) { | 	if (now < expires) { | ||||||
| 		itp->it_value = ns_to_timespec64(timer->it.cpu.expires - now); | 		itp->it_value = ns_to_timespec64(expires - now); | ||||||
| 	} else { | 	} else { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * The timer should have expired already, but the firing | 		 * The timer should have expired already, but the firing | ||||||
|  | @ -735,36 +732,40 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static unsigned long long | #define MAX_COLLECTED	20 | ||||||
| check_timers_list(struct list_head *timers, | 
 | ||||||
| 		  struct list_head *firing, | static u64 collect_timerqueue(struct timerqueue_head *head, | ||||||
| 		  unsigned long long curr) | 			      struct list_head *firing, u64 now) | ||||||
| { | { | ||||||
| 	int maxfire = 20; | 	struct timerqueue_node *next; | ||||||
|  | 	int i = 0; | ||||||
| 
 | 
 | ||||||
| 	while (!list_empty(timers)) { | 	while ((next = timerqueue_getnext(head))) { | ||||||
| 		struct cpu_timer_list *t; | 		struct cpu_timer *ctmr; | ||||||
|  | 		u64 expires; | ||||||
| 
 | 
 | ||||||
| 		t = list_first_entry(timers, struct cpu_timer_list, entry); | 		ctmr = container_of(next, struct cpu_timer, node); | ||||||
|  | 		expires = cpu_timer_getexpires(ctmr); | ||||||
|  | 		/* Limit the number of timers to expire at once */ | ||||||
|  | 		if (++i == MAX_COLLECTED || now < expires) | ||||||
|  | 			return expires; | ||||||
| 
 | 
 | ||||||
| 		if (!--maxfire || curr < t->expires) | 		ctmr->firing = 1; | ||||||
| 			return t->expires; | 		cpu_timer_dequeue(ctmr); | ||||||
| 
 | 		list_add_tail(&ctmr->elist, firing); | ||||||
| 		t->firing = 1; |  | ||||||
| 		list_move_tail(&t->entry, firing); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return U64_MAX; | 	return U64_MAX; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void collect_posix_cputimers(struct posix_cputimers *pct, | static void collect_posix_cputimers(struct posix_cputimers *pct, u64 *samples, | ||||||
| 				    u64 *samples, struct list_head *firing) | 				    struct list_head *firing) | ||||||
| { | { | ||||||
| 	struct posix_cputimer_base *base = pct->bases; | 	struct posix_cputimer_base *base = pct->bases; | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < CPUCLOCK_MAX; i++, base++) { | 	for (i = 0; i < CPUCLOCK_MAX; i++, base++) { | ||||||
| 		base->nextevt = check_timers_list(&base->cpu_timers, firing, | 		base->nextevt = collect_timerqueue(&base->tqhead, firing, | ||||||
| 						    samples[i]); | 						    samples[i]); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -948,7 +949,8 @@ static void check_process_timers(struct task_struct *tsk, | ||||||
| static void posix_cpu_timer_rearm(struct k_itimer *timer) | static void posix_cpu_timer_rearm(struct k_itimer *timer) | ||||||
| { | { | ||||||
| 	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); | 	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); | ||||||
| 	struct task_struct *p = timer->it.cpu.task; | 	struct cpu_timer *ctmr = &timer->it.cpu; | ||||||
|  | 	struct task_struct *p = ctmr->task; | ||||||
| 	struct sighand_struct *sighand; | 	struct sighand_struct *sighand; | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
| 	u64 now; | 	u64 now; | ||||||
|  | @ -980,7 +982,7 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer) | ||||||
| 			 * The process has been reaped. | 			 * The process has been reaped. | ||||||
| 			 * We can't even collect a sample any more. | 			 * We can't even collect a sample any more. | ||||||
| 			 */ | 			 */ | ||||||
| 			timer->it.cpu.expires = 0; | 			cpu_timer_setexpires(ctmr, 0); | ||||||
| 			return; | 			return; | ||||||
| 		} else if (unlikely(p->exit_state) && thread_group_empty(p)) { | 		} else if (unlikely(p->exit_state) && thread_group_empty(p)) { | ||||||
| 			/* If the process is dying, no need to rearm */ | 			/* If the process is dying, no need to rearm */ | ||||||
|  | @ -1124,11 +1126,11 @@ void run_posix_cpu_timers(void) | ||||||
| 	 * each timer's lock before clearing its firing flag, so no | 	 * each timer's lock before clearing its firing flag, so no | ||||||
| 	 * timer call will interfere. | 	 * timer call will interfere. | ||||||
| 	 */ | 	 */ | ||||||
| 	list_for_each_entry_safe(timer, next, &firing, it.cpu.entry) { | 	list_for_each_entry_safe(timer, next, &firing, it.cpu.elist) { | ||||||
| 		int cpu_firing; | 		int cpu_firing; | ||||||
| 
 | 
 | ||||||
| 		spin_lock(&timer->it_lock); | 		spin_lock(&timer->it_lock); | ||||||
| 		list_del_init(&timer->it.cpu.entry); | 		list_del_init(&timer->it.cpu.elist); | ||||||
| 		cpu_firing = timer->it.cpu.firing; | 		cpu_firing = timer->it.cpu.firing; | ||||||
| 		timer->it.cpu.firing = 0; | 		timer->it.cpu.firing = 0; | ||||||
| 		/*
 | 		/*
 | ||||||
|  | @ -1204,6 +1206,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, | ||||||
| 	timer.it_overrun = -1; | 	timer.it_overrun = -1; | ||||||
| 	error = posix_cpu_timer_create(&timer); | 	error = posix_cpu_timer_create(&timer); | ||||||
| 	timer.it_process = current; | 	timer.it_process = current; | ||||||
|  | 
 | ||||||
| 	if (!error) { | 	if (!error) { | ||||||
| 		static struct itimerspec64 zero_it; | 		static struct itimerspec64 zero_it; | ||||||
| 		struct restart_block *restart; | 		struct restart_block *restart; | ||||||
|  | @ -1219,7 +1222,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		while (!signal_pending(current)) { | 		while (!signal_pending(current)) { | ||||||
| 			if (timer.it.cpu.expires == 0) { | 			if (!cpu_timer_getexpires(&timer.it.cpu)) { | ||||||
| 				/*
 | 				/*
 | ||||||
| 				 * Our timer fired and was reset, below | 				 * Our timer fired and was reset, below | ||||||
| 				 * deletion can not fail. | 				 * deletion can not fail. | ||||||
|  | @ -1241,7 +1244,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * We were interrupted by a signal. | 		 * We were interrupted by a signal. | ||||||
| 		 */ | 		 */ | ||||||
| 		expires = timer.it.cpu.expires; | 		expires = cpu_timer_getexpires(&timer.it.cpu); | ||||||
| 		error = posix_cpu_timer_set(&timer, 0, &zero_it, &it); | 		error = posix_cpu_timer_set(&timer, 0, &zero_it, &it); | ||||||
| 		if (!error) { | 		if (!error) { | ||||||
| 			/*
 | 			/*
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Thomas Gleixner
						Thomas Gleixner