source: rtems/cpukit/posix/src/killinfo.c @ 66374df

5
Last change on this file since 66374df was 66374df, checked in by Sebastian Huber <sebastian.huber@…>, on 04/29/16 at 07:27:11

posix: Avoid Giant lock in _POSIX_signals_Send()

Update #2555.
Update #2690.

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/**
2 * @file
3 *
4 * @brief Send a Signal to a Process
5 * @ingroup POSIXAPI
6 */
7
8/*
9 *  kill() support routine
10 *
11 *  COPYRIGHT (c) 1989-2009.
12 *  On-Line Applications Research Corporation (OAR).
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.org/license/LICENSE.
17 */
18
19#if HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <pthread.h>
24#include <signal.h>
25#include <errno.h>
26
27#include <rtems/posix/pthreadimpl.h>
28#include <rtems/posix/psignalimpl.h>
29#include <rtems/score/isr.h>
30#include <rtems/score/statesimpl.h>
31#include <rtems/seterr.h>
32
33/*
34 *  If you enable this, then you get printk() feedback on each path
35 *  and the input to the decision that lead to the decision.  Hopefully
36 *  this will help in debugging the algorithm that distributes process
37 *  signals to individual threads.
38 */
39
40/* #define DEBUG_SIGNAL_PROCESSING */
41#if defined(DEBUG_SIGNAL_PROCESSING)
42  #include <rtems/bspIo.h>
43  #define DEBUG_STEP(_x) printk(_x)
44#else
45  #define DEBUG_STEP(_x)
46#endif
47
48/*
49 *  3.3.2 Send a Signal to a Process, P1003.1b-1993, p. 68
50 *
51 *  NOTE: Behavior of kill() depends on _POSIX_SAVED_IDS.
52 */
53
54#define _POSIX_signals_Is_interested( _api, _mask ) \
55  ( (_api)->signals_unblocked & (_mask) )
56
57int _POSIX_signals_Send(
58  pid_t               pid,
59  int                 sig,
60  const union sigval *value
61)
62{
63  sigset_t                     mask;
64  POSIX_API_Control           *api;
65  uint32_t                     the_api;
66  uint32_t                     index;
67  uint32_t                     maximum;
68  Objects_Information         *the_info;
69  Objects_Control            **object_table;
70  Thread_Control              *the_thread;
71  Thread_Control              *interested;
72  Priority_Control             interested_priority;
73  Chain_Node                  *the_node;
74  siginfo_t                    siginfo_struct;
75  siginfo_t                   *siginfo;
76  POSIX_signals_Siginfo_node  *psiginfo;
77  Thread_queue_Heads          *heads;
78  ISR_lock_Context             lock_context;
79  Per_CPU_Control             *cpu_self;
80
81  /*
82   *  Only supported for the "calling process" (i.e. this node).
83   */
84  if ( pid != getpid() )
85    rtems_set_errno_and_return_minus_one( ESRCH );
86
87  /*
88   *  Validate the signal passed.
89   */
90  if ( !sig )
91    rtems_set_errno_and_return_minus_one( EINVAL );
92
93  if ( !is_valid_signo(sig) )
94    rtems_set_errno_and_return_minus_one( EINVAL );
95
96  /*
97   *  If the signal is being ignored, then we are out of here.
98   */
99  if ( _POSIX_signals_Vectors[ sig ].sa_handler == SIG_IGN )
100    return 0;
101
102  /*
103   *  P1003.1c/Draft 10, p. 33 says that certain signals should always
104   *  be directed to the executing thread such as those caused by hardware
105   *  faults.
106   */
107  if ( (sig == SIGFPE) || (sig == SIGILL) || (sig == SIGSEGV ) )
108      return pthread_kill( pthread_self(), sig );
109
110  mask = signo_to_mask( sig );
111
112  /*
113   *  Build up a siginfo structure
114   */
115  siginfo = &siginfo_struct;
116  siginfo->si_signo = sig;
117  siginfo->si_code = SI_USER;
118  if ( !value ) {
119    siginfo->si_value.sival_int = 0;
120  } else {
121    siginfo->si_value = *value;
122  }
123
124  /* FIXME: https://devel.rtems.org/ticket/2690 */
125  cpu_self = _Thread_Dispatch_disable();
126
127  /*
128   *  Is the currently executing thread interested?  If so then it will
129   *  get it an execute it as soon as the dispatcher executes.
130   */
131  the_thread = _Per_CPU_Get_executing( cpu_self );
132
133  api = the_thread->API_Extensions[ THREAD_API_POSIX ];
134  if ( _POSIX_signals_Is_interested( api, mask ) ) {
135    goto process_it;
136  }
137
138  /*
139   *  Is an interested thread waiting for this signal (sigwait())?
140   *
141   *  There is no requirement on the order of threads pending on a sigwait().
142   */
143
144  /* XXX violation of visibility -- need to define thread queue support */
145
146  heads = _POSIX_signals_Wait_queue.Queue.heads;
147  if ( heads != NULL ) {
148    Chain_Control *the_chain = &heads->Heads.Fifo;
149
150    for ( the_node = _Chain_First( the_chain );
151          !_Chain_Is_tail( the_chain, the_node ) ;
152          the_node = the_node->next ) {
153
154      the_thread = THREAD_CHAIN_NODE_TO_THREAD( the_node );
155      api = the_thread->API_Extensions[ THREAD_API_POSIX ];
156
157      #if defined(DEBUG_SIGNAL_PROCESSING)
158        printk( "Waiting Thread=%p option=0x%08x mask=0x%08x blocked=0x%08x\n",
159          the_thread, the_thread->Wait.option, mask, ~api->signals_unblocked);
160      #endif
161
162      /*
163       * Is this thread is actually blocked waiting for the signal?
164       */
165      if (the_thread->Wait.option & mask)
166        goto process_it;
167
168      /*
169       * Is this thread is blocked waiting for another signal but has
170       * not blocked this one?
171       */
172      if (api->signals_unblocked & mask)
173        goto process_it;
174    }
175  }
176
177  /*
178   *  Is any other thread interested?  The highest priority interested
179   *  thread is selected.  In the event of a tie, then the following
180   *  additional criteria is used:
181   *
182   *    + ready thread over blocked
183   *    + blocked on call interruptible by signal (can return EINTR)
184   *    + blocked on call not interruptible by signal
185   *
186   *  This looks at every thread in the system regardless of the creating API.
187   *
188   *  NOTES:
189   *
190   *    + rtems internal threads do not receive signals.
191   */
192  interested = NULL;
193  interested_priority = PRIORITY_MAXIMUM + 1;
194
195  for (the_api = OBJECTS_CLASSIC_API; the_api <= OBJECTS_APIS_LAST; the_api++) {
196
197    /*
198     *  This can occur when no one is interested and an API is not configured.
199     */
200    if ( !_Objects_Information_table[ the_api ] )
201      continue;
202
203    the_info = _Objects_Information_table[ the_api ][ 1 ];
204    if ( !the_info )
205      continue;
206
207    maximum = the_info->maximum;
208    object_table = the_info->local_table;
209
210    for ( index = 1 ; index <= maximum ; index++ ) {
211      the_thread = (Thread_Control *) object_table[ index ];
212
213      if ( !the_thread )
214        continue;
215
216      #if defined(DEBUG_SIGNAL_PROCESSING)
217        printk("\n 0x%08x/0x%08x %d/%d 0x%08x 1",
218          the_thread->Object.id,
219          ((interested) ? interested->Object.id : 0),
220          the_thread->current_priority, interested_priority,
221          the_thread->current_state
222        );
223      #endif
224
225      /*
226       *  If this thread is of lower priority than the interested thread,
227       *  go on to the next thread.
228       */
229      if ( the_thread->current_priority > interested_priority )
230        continue;
231      DEBUG_STEP("2");
232
233      /*
234       *  If this thread is not interested, then go on to the next thread.
235       */
236      api = the_thread->API_Extensions[ THREAD_API_POSIX ];
237
238      #if defined(RTEMS_DEBUG)
239        if ( !api )
240          continue;
241      #endif
242
243      if ( !_POSIX_signals_Is_interested( api, mask ) )
244        continue;
245      DEBUG_STEP("3");
246
247      /*
248       *  Now we know the thread under consideration is interested.
249       *  If the thread under consideration is of higher priority, then
250       *  it becomes the interested thread.
251       *
252       *  NOTE: We initialized interested_priority to PRIORITY_MAXIMUM + 1
253       *        so we never have to worry about deferencing a NULL
254       *        interested thread.
255       */
256      if ( the_thread->current_priority < interested_priority ) {
257        interested   = the_thread;
258        interested_priority = the_thread->current_priority;
259        continue;
260      }
261      DEBUG_STEP("4");
262
263      /*
264       *  Now the thread and the interested thread have the same priority.
265       *  We have to sort through the combinations of blocked/not blocked
266       *  and blocking interruptibutable by signal.
267       *
268       *  If the interested thread is ready, don't think about changing.
269       */
270
271      if ( interested && !_States_Is_ready( interested->current_state ) ) {
272        /* preferred ready over blocked */
273        DEBUG_STEP("5");
274        if ( _States_Is_ready( the_thread->current_state ) ) {
275          interested          = the_thread;
276          interested_priority = the_thread->current_priority;
277          continue;
278        }
279
280        DEBUG_STEP("6");
281        /* prefer blocked/interruptible over blocked/not interruptible */
282        if ( !_States_Is_interruptible_by_signal(interested->current_state) ) {
283          DEBUG_STEP("7");
284          if ( _States_Is_interruptible_by_signal(the_thread->current_state) ) {
285            DEBUG_STEP("8");
286            interested          = the_thread;
287            interested_priority = the_thread->current_priority;
288            continue;
289          }
290        }
291      }
292    }
293  }
294
295  if ( interested ) {
296    the_thread = interested;
297    goto process_it;
298  }
299
300  /*
301   *  OK so no threads were interested right now.  It will be left on the
302   *  global pending until a thread receives it.  The global set of threads
303   *  can change interest in this signal in one of the following ways:
304   *
305   *    + a thread is created with the signal unblocked,
306   *    + pthread_sigmask() unblocks the signal,
307   *    + sigprocmask() unblocks the signal, OR
308   *    + sigaction() which changes the handler to SIG_IGN.
309   */
310  the_thread = NULL;
311  goto post_process_signal;
312
313  /*
314   *  We found a thread which was interested, so now we mark that this
315   *  thread needs to do the post context switch extension so it can
316   *  evaluate the signals pending.
317   */
318process_it:
319
320  /*
321   *  Returns true if the signal was synchronously given to a thread
322   *  blocked waiting for the signal.
323   */
324  if ( _POSIX_signals_Unblock_thread( the_thread, sig, siginfo ) ) {
325    _Thread_Dispatch_enable( cpu_self );
326    return 0;
327  }
328
329post_process_signal:
330
331  /*
332   *  We may have woken up a thread but we definitely need to post the
333   *  signal to the process wide information set.
334   */
335  _POSIX_signals_Set_process_signals( mask );
336
337  _POSIX_signals_Acquire( &lock_context );
338
339  if ( _POSIX_signals_Vectors[ sig ].sa_flags == SA_SIGINFO ) {
340
341    psiginfo = (POSIX_signals_Siginfo_node *)
342      _Chain_Get_unprotected( &_POSIX_signals_Inactive_siginfo );
343    if ( !psiginfo ) {
344      _POSIX_signals_Release( &lock_context );
345      _Thread_Dispatch_enable( cpu_self );
346      rtems_set_errno_and_return_minus_one( EAGAIN );
347    }
348
349    psiginfo->Info = *siginfo;
350
351    _Chain_Append_unprotected(
352      &_POSIX_signals_Siginfo[ sig ],
353      &psiginfo->Node
354    );
355  }
356
357  _POSIX_signals_Release( &lock_context );
358  DEBUG_STEP("\n");
359  _Thread_Dispatch_enable( cpu_self );
360  return 0;
361}
Note: See TracBrowser for help on using the repository browser.