Commit d1791d31 authored by Jim Houston's avatar Jim Houston Committed by Ben Collins

[PATCH] preallocate signal queue resource - Posix timers

This adds a new interface to kernel/signal.c which allows signals to be
sent using preallocated sigqueue structures.  It also modifies
kernel/posix-timers.c to use this interface.

The current timer code may fail to deliver a timer expiry signal if
there are no sigqueue structures available at the time of the expiry.
The Posix specification is clear that the signal queuing resource should
be allocated at timer_create time.  This allows the error to be returned
to the application rather than silently losing the signal.

This patch does not change the sigqueue structure allocation policy.  I
hope to revisit that in another patch.

Here is the definition for the new interface:

struct sigqueue *sigqueue_alloc(void)
	Preallocate a sigqueue structure for use with the functions
	described below.

void sigqueue_free(struct sigqueue *q)
	Free a preallocated sigqueue structure.  If the sigqueue
	structure being freed is still queued, it will be removed
	from the queue.  I currently leave the signal pending.
	It may be delivered without the siginfo structure.

int send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
	This function is equivalent to send_sig_info().  It queues
	a signal to the specified thread using  the supplied sigqueue
	structure.  The caller is expected to fill in the siginfo_t
	which is part of the sigqueue structure.

int send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
	This function is equivalent to send_group_sig_info().  It queues
	the signal to a process allowing the system to select which thread
	will receive the signal in a multi-threaded process.
	Again, the sigqueue structure is used to queue the signal.

Both send_sigqueue() and send_group_sigqueue() return 0 if the signal
is queued. They return 1 if the signal was not queued because the
process is ignoring the signal.

Both versions include code to increment the si_overrun count if the
sigqueue entry is for a Posix timer and they are called while the
sigqueue entry is still queued.  Yes, I know that the current code
doesn't rearm the timer until the signal is delivered.  Having this
extra bit of code doesn't do any harm, and I plan to use it.

These routines do not check if there already is a legacy (non-realtime)
signal pending.  They always queue the signal.  This requires that
collect_signal() always checks if there is another matching siginfo
before clearing the signal bit.
parent 76621ec6
...@@ -45,7 +45,9 @@ ...@@ -45,7 +45,9 @@
#define INIT_SIGNALS(sig) { \ #define INIT_SIGNALS(sig) { \
.count = ATOMIC_INIT(1), \ .count = ATOMIC_INIT(1), \
.shared_pending = { NULL, &sig.shared_pending.head, {{0}}}, \ .shared_pending = { \
.list = LIST_HEAD_INIT(sig.shared_pending.list), \
.signal = {{0}}}, \
} }
#define INIT_SIGHAND(sighand) { \ #define INIT_SIGHAND(sighand) { \
...@@ -97,7 +99,9 @@ ...@@ -97,7 +99,9 @@
.files = &init_files, \ .files = &init_files, \
.signal = &init_signals, \ .signal = &init_signals, \
.sighand = &init_sighand, \ .sighand = &init_sighand, \
.pending = { NULL, &tsk.pending.head, {{0}}}, \ .pending = { \
.list = LIST_HEAD_INIT(tsk.pending.list), \
.signal = {{0}}}, \
.blocked = {{0}}, \ .blocked = {{0}}, \
.posix_timers = LIST_HEAD_INIT(tsk.posix_timers), \ .posix_timers = LIST_HEAD_INIT(tsk.posix_timers), \
.alloc_lock = SPIN_LOCK_UNLOCKED, \ .alloc_lock = SPIN_LOCK_UNLOCKED, \
......
...@@ -317,6 +317,7 @@ struct k_itimer { ...@@ -317,6 +317,7 @@ struct k_itimer {
unsigned long it_incr; /* interval specified in jiffies */ unsigned long it_incr; /* interval specified in jiffies */
struct task_struct *it_process; /* process to send signal to */ struct task_struct *it_process; /* process to send signal to */
struct timer_list it_timer; struct timer_list it_timer;
struct sigqueue *sigq; /* signal queue entry. */
}; };
...@@ -571,6 +572,10 @@ extern void zap_other_threads(struct task_struct *p); ...@@ -571,6 +572,10 @@ extern void zap_other_threads(struct task_struct *p);
extern int kill_pg(pid_t, int, int); extern int kill_pg(pid_t, int, int);
extern int kill_sl(pid_t, int, int); extern int kill_sl(pid_t, int, int);
extern int kill_proc(pid_t, int, int); extern int kill_proc(pid_t, int, int);
extern struct sigqueue *sigqueue_alloc(void);
extern void sigqueue_free(struct sigqueue *);
extern int send_sigqueue(int, struct sigqueue *, struct task_struct *);
extern int send_group_sigqueue(int, struct sigqueue *, struct task_struct *);
extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *); extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *);
extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long); extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <asm/signal.h> #include <asm/signal.h>
#include <asm/siginfo.h> #include <asm/siginfo.h>
#include <linux/list.h>
#ifdef __KERNEL__ #ifdef __KERNEL__
/* /*
...@@ -10,12 +11,17 @@ ...@@ -10,12 +11,17 @@
*/ */
struct sigqueue { struct sigqueue {
struct sigqueue *next; struct list_head list;
spinlock_t *lock;
int flags;
siginfo_t info; siginfo_t info;
}; };
/* flags values. */
#define SIGQUEUE_PREALLOC 1
struct sigpending { struct sigpending {
struct sigqueue *head, **tail; struct list_head list;
sigset_t signal; sigset_t signal;
}; };
...@@ -197,8 +203,7 @@ static inline void siginitsetinv(sigset_t *set, unsigned long mask) ...@@ -197,8 +203,7 @@ static inline void siginitsetinv(sigset_t *set, unsigned long mask)
static inline void init_sigpending(struct sigpending *sig) static inline void init_sigpending(struct sigpending *sig)
{ {
sigemptyset(&sig->signal); sigemptyset(&sig->signal);
sig->head = NULL; INIT_LIST_HEAD(&sig->list);
sig->tail = &sig->head;
} }
extern long do_sigpending(void __user *, unsigned long); extern long do_sigpending(void __user *, unsigned long);
......
...@@ -288,46 +288,32 @@ void do_schedule_next_timer(struct siginfo *info) ...@@ -288,46 +288,32 @@ void do_schedule_next_timer(struct siginfo *info)
static void timer_notify_task(struct k_itimer *timr) static void timer_notify_task(struct k_itimer *timr)
{ {
struct siginfo info;
int ret; int ret;
memset(&info, 0, sizeof (info)); memset(&timr->sigq->info, 0, sizeof(siginfo_t));
/* Send signal to the process that owns this timer. */ /* Send signal to the process that owns this timer. */
info.si_signo = timr->it_sigev_signo; timr->sigq->info.si_signo = timr->it_sigev_signo;
info.si_errno = 0; timr->sigq->info.si_errno = 0;
info.si_code = SI_TIMER; timr->sigq->info.si_code = SI_TIMER;
info.si_tid = timr->it_id; timr->sigq->info.si_tid = timr->it_id;
info.si_value = timr->it_sigev_value; timr->sigq->info.si_value = timr->it_sigev_value;
if (timr->it_incr) if (timr->it_incr)
info.si_sys_private = ++timr->it_requeue_pending; timr->sigq->info.si_sys_private = ++timr->it_requeue_pending;
if (timr->it_sigev_notify & SIGEV_THREAD_ID & MIPS_SIGEV) if (timr->it_sigev_notify & SIGEV_THREAD_ID & MIPS_SIGEV)
ret = send_sig_info(info.si_signo, &info, timr->it_process); ret = send_sigqueue(timr->it_sigev_signo, timr->sigq,
timr->it_process);
else else
ret = send_group_sig_info(info.si_signo, &info, ret = send_group_sigqueue(timr->it_sigev_signo, timr->sigq,
timr->it_process); timr->it_process);
switch (ret) { if (ret) {
default:
/* /*
* Signal was not sent. May or may not need to * signal was not sent because of sig_ignor
* restart the timer.
*/
printk(KERN_WARNING "sending signal failed: %d\n", ret);
case 1:
/*
* signal was not sent because of sig_ignor or,
* possibly no queue memory OR will be sent but,
* we will not get a call back to restart it AND * we will not get a call back to restart it AND
* it should be restarted. * it should be restarted.
*/ */
schedule_next_timer(timr); schedule_next_timer(timr);
case 0:
/*
* all's well new signal queued
*/
break;
} }
} }
...@@ -379,7 +365,11 @@ static struct k_itimer * alloc_posix_timer(void) ...@@ -379,7 +365,11 @@ static struct k_itimer * alloc_posix_timer(void)
struct k_itimer *tmr; struct k_itimer *tmr;
tmr = kmem_cache_alloc(posix_timers_cache, GFP_KERNEL); tmr = kmem_cache_alloc(posix_timers_cache, GFP_KERNEL);
memset(tmr, 0, sizeof (struct k_itimer)); memset(tmr, 0, sizeof (struct k_itimer));
tmr->it_id = (timer_t)-1;
if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
kmem_cache_free(posix_timers_cache, tmr);
tmr = 0;
}
return tmr; return tmr;
} }
...@@ -390,6 +380,7 @@ static void release_posix_timer(struct k_itimer *tmr) ...@@ -390,6 +380,7 @@ static void release_posix_timer(struct k_itimer *tmr)
idr_remove(&posix_timers_id, tmr->it_id); idr_remove(&posix_timers_id, tmr->it_id);
spin_unlock_irq(&idr_lock); spin_unlock_irq(&idr_lock);
} }
sigqueue_free(tmr->sigq);
kmem_cache_free(posix_timers_cache, tmr); kmem_cache_free(posix_timers_cache, tmr);
} }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment