Commit 0360ed14 authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

signal: Refactor send_sigqueue()



To handle posix timers which have their signal ignored via SIG_IGN properly
it is required to requeue a ignored signal for delivery when SIG_IGN is
lifted so the timer gets rearmed.

Split the required code out of send_sigqueue() so it can be reused in
context of sigaction().

While at it rename send_sigqueue() to posixtimer_send_sigqueue() so its
clear what this is about.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarFrederic Weisbecker <frederic@kernel.org>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.586453412@linutronix.de
parent ef1c5bcd
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,

void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_init_sigqueue(struct sigqueue *q);
int posixtimer_send_sigqueue(struct k_itimer *tmr);
bool posixtimer_deliver_signal(struct kernel_siginfo *info);
void posixtimer_free_timer(struct k_itimer *timer);

+0 −1
Original line number Diff line number Diff line
@@ -340,7 +340,6 @@ extern int send_sig(int, struct task_struct *, int);
extern int zap_other_threads(struct task_struct *p);
extern struct sigqueue *sigqueue_alloc(void);
extern void sigqueue_free(struct sigqueue *);
extern int send_sigqueue(struct sigqueue *, struct pid *, enum pid_type, int si_private);
extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *);

static inline void clear_notify_signal(void)
+45 −37
Original line number Diff line number Diff line
@@ -1947,21 +1947,17 @@ void sigqueue_free(struct sigqueue *q)
		__sigqueue_free(q);
}

int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int si_private)
static void posixtimer_queue_sigqueue(struct sigqueue *q, struct task_struct *t, enum pid_type type)
{
	int sig = q->info.si_signo;
	struct sigpending *pending;
	struct task_struct *t;
	unsigned long flags;
	int ret, result;

	if (WARN_ON_ONCE(!(q->flags & SIGQUEUE_PREALLOC)))
		return 0;
	if (WARN_ON_ONCE(q->info.si_code != SI_TIMER))
		return 0;
	int sig = q->info.si_signo;

	ret = -1;
	rcu_read_lock();
	signalfd_notify(t, sig);
	pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
	list_add_tail(&q->list, &pending->list);
	sigaddset(&pending->signal, sig);
	complete_signal(sig, t, type);
}

/*
 * This function is used by POSIX timers to deliver a timer signal.
@@ -1974,13 +1970,31 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int s
 * the same thread group as the target process, which avoids
 * unnecessarily waking up a potentially idle task.
 */
	t = pid_task(pid, type);
	if (!t)
		goto ret;
	if (type != PIDTYPE_PID && same_thread_group(t, current))
static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr)
{
	struct task_struct *t = pid_task(tmr->it_pid, tmr->it_pid_type);

	if (t && tmr->it_pid_type != PIDTYPE_PID && same_thread_group(t, current))
		t = current;
	return t;
}

int posixtimer_send_sigqueue(struct k_itimer *tmr)
{
	struct sigqueue *q = tmr->sigq;
	int sig = q->info.si_signo;
	struct task_struct *t;
	unsigned long flags;
	int ret, result;

	guard(rcu)();

	t = posixtimer_get_target(tmr);
	if (!t)
		return -1;

	if (!likely(lock_task_sighand(t, &flags)))
		goto ret;
		return -1;

	/*
	 * Update @q::info::si_sys_private for posix timer signals with
@@ -1988,30 +2002,24 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int s
	 * decides based on si_sys_private whether to invoke
	 * posixtimer_rearm() or not.
	 */
	q->info.si_sys_private = si_private;
	q->info.si_sys_private = tmr->it_signal_seq;

	ret = 1; /* the signal is ignored */
	if (!prepare_signal(sig, t, false)) {
		result = TRACE_SIGNAL_IGNORED;
	if (!prepare_signal(sig, t, false))
		goto out;
	}

	ret = 0;
	if (unlikely(!list_empty(&q->list))) {
		result = TRACE_SIGNAL_ALREADY_PENDING;
		goto out;
	}

	signalfd_notify(t, sig);
	pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
	list_add_tail(&q->list, &pending->list);
	sigaddset(&pending->signal, sig);
	complete_signal(sig, t, type);
	posixtimer_queue_sigqueue(q, t, tmr->it_pid_type);
	result = TRACE_SIGNAL_DELIVERED;
out:
	trace_signal_generate(sig, &q->info, t, type != PIDTYPE_PID, result);
	trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
	unlock_task_sighand(t, &flags);
ret:
	rcu_read_unlock();
	return ret;
}
#endif /* CONFIG_POSIX_TIMERS */
+1 −1
Original line number Diff line number Diff line
@@ -307,7 +307,7 @@ int posix_timer_queue_signal(struct k_itimer *timr)

	timr->it_status = state;

	ret = send_sigqueue(timr->sigq, timr->it_pid, timr->it_pid_type, timr->it_signal_seq);
	ret = posixtimer_send_sigqueue(timr);
	/* If we failed to send the signal the timer stops. */
	return ret > 0;
}