Re: FAILED: patch "[PATCH] posix-cpu-timers: Store a reference to a pid not a task" failed to apply to 5.6-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



<gregkh@xxxxxxxxxxxxxxxxxxx> writes:

> The patch below does not apply to the 5.6-stable tree.
> If someone wants it applied there, or to any other stable or longterm
> tree, then please email the backport, including the original git commit
> id to <stable@xxxxxxxxxxxxxxx>.

So for anyone who cares the fix I refer to in the commit comment is the
workaround that keeps the past implementation from being a real problem.

I just see this as code cleanup so I can remove the old workaround.  The
old workaround will cause posix_cpu_timers_exit_group to be called early
on particular variants of multi-threaded exec, resulting in the
corresponding cpu clock stopping.  So this does represent a real fix.

However using a cpu timer of another process to signal things in your
process is rare, and the case is breaks is only in certain obscure
variations of a multi-threaded exec.  Further no one has to my knowledge
complained in over a decade.

If someone sees that fix as important, and something that needs to be
backported, it will be easiest to backport my earlier cleanup patches
in the same series.

Eric


> thanks,
>
> greg k-h
>
> ------------------ original commit in Linus's tree ------------------
>
> From 55e8c8eb2c7b6bf30e99423ccfe7ca032f498f59 Mon Sep 17 00:00:00 2001
> From: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx>
> Date: Fri, 28 Feb 2020 11:11:06 -0600
> Subject: [PATCH] posix-cpu-timers: Store a reference to a pid not a task
>
> posix cpu timers do not handle the death of a process well.
>
> This is most clearly seen when a multi-threaded process calls exec from a
> thread that is not the leader of the thread group.  The posix cpu timer code
> continues to pin the old thread group leader and is unable to find the
> siglock from there.
>
> This results in posix_cpu_timer_del being unable to delete a timer,
> posix_cpu_timer_set being unable to set a timer.  Further to compensate for
> the problems in posix_cpu_timer_del on a multi-threaded exec all timers
> that point at the multi-threaded task are stopped.
>
> The code for the timers fundamentally needs to check if the target
> process/thread is alive.  This needs an extra level of indirection. This
> level of indirection is already available in struct pid.
>
> So replace cpu.task with cpu.pid to get the needed extra layer of
> indirection.
>
> In addition to handling things more cleanly this reduces the amount of
> memory a timer can pin when a process exits and then is reaped from
> a task_struct to the vastly smaller struct pid.
>
> Fixes: e0a70217107e ("posix-cpu-timers: workaround to suppress the problems with mt exec")
> Signed-off-by: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx>
> Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> Link: https://lkml.kernel.org/r/87wo86tz6d.fsf@xxxxxxxxxxxxxxxxxxxxx
>
> diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
> index 3d10c84a97a9..e3f0f8585da4 100644
> --- a/include/linux/posix-timers.h
> +++ b/include/linux/posix-timers.h
> @@ -69,7 +69,7 @@ static inline int clockid_to_fd(const clockid_t clk)
>  struct cpu_timer {
>  	struct timerqueue_node	node;
>  	struct timerqueue_head	*head;
> -	struct task_struct	*task;
> +	struct pid		*pid;
>  	struct list_head	elist;
>  	int			firing;
>  };
> diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
> index ef936c5a910b..6df468a622fe 100644
> --- a/kernel/time/posix-cpu-timers.c
> +++ b/kernel/time/posix-cpu-timers.c
> @@ -118,6 +118,16 @@ static inline int validate_clock_permissions(const clockid_t clock)
>  	return __get_task_for_clock(clock, false, false) ? 0 : -EINVAL;
>  }
>  
> +static inline enum pid_type cpu_timer_pid_type(struct k_itimer *timer)
> +{
> +	return CPUCLOCK_PERTHREAD(timer->it_clock) ? PIDTYPE_PID : PIDTYPE_TGID;
> +}
> +
> +static inline struct task_struct *cpu_timer_task_rcu(struct k_itimer *timer)
> +{
> +	return pid_task(timer->it.cpu.pid, cpu_timer_pid_type(timer));
> +}
> +
>  /*
>   * Update expiry time from increment, and increase overrun count,
>   * given the current clock sample.
> @@ -391,7 +401,12 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer)
>  
>  	new_timer->kclock = &clock_posix_cpu;
>  	timerqueue_init(&new_timer->it.cpu.node);
> -	new_timer->it.cpu.task = p;
> +	new_timer->it.cpu.pid = get_task_pid(p, cpu_timer_pid_type(new_timer));
> +	/*
> +	 * get_task_for_clock() took a reference on @p. Drop it as the timer
> +	 * holds a reference on the pid of @p.
> +	 */
> +	put_task_struct(p);
>  	return 0;
>  }
>  
> @@ -404,13 +419,15 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer)
>  static int posix_cpu_timer_del(struct k_itimer *timer)
>  {
>  	struct cpu_timer *ctmr = &timer->it.cpu;
> -	struct task_struct *p = ctmr->task;
>  	struct sighand_struct *sighand;
> +	struct task_struct *p;
>  	unsigned long flags;
>  	int ret = 0;
>  
> -	if (WARN_ON_ONCE(!p))
> -		return -EINVAL;
> +	rcu_read_lock();
> +	p = cpu_timer_task_rcu(timer);
> +	if (!p)
> +		goto out;
>  
>  	/*
>  	 * Protect against sighand release/switch in exit/exec and process/
> @@ -432,8 +449,10 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
>  		unlock_task_sighand(p, &flags);
>  	}
>  
> +out:
> +	rcu_read_unlock();
>  	if (!ret)
> -		put_task_struct(p);
> +		put_pid(ctmr->pid);
>  
>  	return ret;
>  }
> @@ -561,13 +580,21 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
>  	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
>  	u64 old_expires, new_expires, old_incr, val;
>  	struct cpu_timer *ctmr = &timer->it.cpu;
> -	struct task_struct *p = ctmr->task;
>  	struct sighand_struct *sighand;
> +	struct task_struct *p;
>  	unsigned long flags;
>  	int ret = 0;
>  
> -	if (WARN_ON_ONCE(!p))
> -		return -EINVAL;
> +	rcu_read_lock();
> +	p = cpu_timer_task_rcu(timer);
> +	if (!p) {
> +		/*
> +		 * If p has just been reaped, we can no
> +		 * longer get any information about it at all.
> +		 */
> +		rcu_read_unlock();
> +		return -ESRCH;
> +	}
>  
>  	/*
>  	 * Use the to_ktime conversion because that clamps the maximum
> @@ -584,8 +611,10 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
>  	 * If p has just been reaped, we can no
>  	 * longer get any information about it at all.
>  	 */
> -	if (unlikely(sighand == NULL))
> +	if (unlikely(sighand == NULL)) {
> +		rcu_read_unlock();
>  		return -ESRCH;
> +	}
>  
>  	/*
>  	 * Disarm any old timer after extracting its expiry time.
> @@ -690,6 +719,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
>  
>  	ret = 0;
>   out:
> +	rcu_read_unlock();
>  	if (old)
>  		old->it_interval = ns_to_timespec64(old_incr);
>  
> @@ -701,10 +731,12 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp
>  	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
>  	struct cpu_timer *ctmr = &timer->it.cpu;
>  	u64 now, expires = cpu_timer_getexpires(ctmr);
> -	struct task_struct *p = ctmr->task;
> +	struct task_struct *p;
>  
> -	if (WARN_ON_ONCE(!p))
> -		return;
> +	rcu_read_lock();
> +	p = cpu_timer_task_rcu(timer);
> +	if (!p)
> +		goto out;
>  
>  	/*
>  	 * Easy part: convert the reload time.
> @@ -712,7 +744,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp
>  	itp->it_interval = ktime_to_timespec64(timer->it_interval);
>  
>  	if (!expires)
> -		return;
> +		goto out;
>  
>  	/*
>  	 * Sample the clock to take the difference with the expiry time.
> @@ -732,6 +764,8 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp
>  		itp->it_value.tv_nsec = 1;
>  		itp->it_value.tv_sec = 0;
>  	}
> +out:
> +	rcu_read_unlock();
>  }
>  
>  #define MAX_COLLECTED	20
> @@ -952,14 +986,15 @@ static void check_process_timers(struct task_struct *tsk,
>  static void posix_cpu_timer_rearm(struct k_itimer *timer)
>  {
>  	clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
> -	struct cpu_timer *ctmr = &timer->it.cpu;
> -	struct task_struct *p = ctmr->task;
> +	struct task_struct *p;
>  	struct sighand_struct *sighand;
>  	unsigned long flags;
>  	u64 now;
>  
> -	if (WARN_ON_ONCE(!p))
> -		return;
> +	rcu_read_lock();
> +	p = cpu_timer_task_rcu(timer);
> +	if (!p)
> +		goto out;
>  
>  	/*
>  	 * Fetch the current sample and update the timer's expiry time.
> @@ -974,13 +1009,15 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
>  	/* Protect timer list r/w in arm_timer() */
>  	sighand = lock_task_sighand(p, &flags);
>  	if (unlikely(sighand == NULL))
> -		return;
> +		goto out;
>  
>  	/*
>  	 * Now re-arm for the new expiry time.
>  	 */
>  	arm_timer(timer, p);
>  	unlock_task_sighand(p, &flags);
> +out:
> +	rcu_read_unlock();
>  }
>  
>  /**



[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux