source: rtems/cpukit/score/include/rtems/score/smplock.h @ 64939bc

4.115
Last change on this file since 64939bc was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

  • Property mode set to 100644
File size: 15.7 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup ScoreSMPLock
5 *
6 * @brief SMP Lock API
7 */
8
9/*
10 * COPYRIGHT (c) 1989-2011.
11 * On-Line Applications Research Corporation (OAR).
12 *
13 * Copyright (c) 2013-2014 embedded brains GmbH
14 *
15 * The license and distribution terms for this file may be
16 * found in the file LICENSE in this distribution or at
17 * http://www.rtems.org/license/LICENSE.
18 */
19
20#ifndef _RTEMS_SCORE_SMPLOCK_H
21#define _RTEMS_SCORE_SMPLOCK_H
22
23#include <rtems/score/cpuopts.h>
24
25#if defined( RTEMS_SMP )
26
27#include <rtems/score/atomic.h>
28#include <rtems/score/isrlevel.h>
29
30#if defined( RTEMS_PROFILING )
31#include <rtems/score/chainimpl.h>
32#include <string.h>
33#endif
34
35#ifdef __cplusplus
36extern "C" {
37#endif /* __cplusplus */
38
39/**
40 * @defgroup ScoreSMPLock SMP Locks
41 *
42 * @ingroup Score
43 *
44 * @brief The SMP lock provides mutual exclusion for SMP systems at the lowest
45 * level.
46 *
47 * The SMP lock is implemented as a ticket lock.  This provides fairness in
48 * case of concurrent lock attempts.
49 *
50 * This SMP lock API uses a local context for acquire and release pairs.  Such
51 * a context may be used to implement for example the Mellor-Crummey and Scott
52 * (MCS) locks in the future.
53 *
54 * @{
55 */
56
57/**
58 * @brief Count of lock contention counters for lock statistics.
59 */
60#define SMP_LOCK_STATS_CONTENTION_COUNTS 4
61
62/**
63 * @brief SMP lock statistics.
64 *
65 * The lock acquire attempt instant is the point in time right after the
66 * interrupt disable action in the lock acquire sequence.
67 *
68 * The lock acquire instant is the point in time right after the lock
69 * acquisition.  This is the begin of the critical section code execution.
70 *
71 * The lock release instant is the point in time right before the interrupt
72 * enable action in the lock release sequence.
73 *
74 * The lock section time is the time elapsed between the lock acquire instant
75 * and the lock release instant.
76 *
77 * The lock acquire time is the time elapsed between the lock acquire attempt
78 * instant and the lock acquire instant.
79 */
80typedef struct {
81#if defined( RTEMS_PROFILING )
82  /**
83   * @brief Node for SMP lock statistics chain.
84   */
85  Chain_Node Node;
86
87  /**
88   * @brief The maximum lock acquire time in CPU counter ticks.
89   */
90  CPU_Counter_ticks max_acquire_time;
91
92  /**
93   * @brief The maximum lock section time in CPU counter ticks.
94   */
95  CPU_Counter_ticks max_section_time;
96
97  /**
98   * @brief The count of lock uses.
99   *
100   * This value may overflow.
101   */
102  uint64_t usage_count;
103
104  /**
105   * @brief Total lock acquire time in nanoseconds.
106   *
107   * The average lock acquire time is the total acquire time divided by the
108   * lock usage count.  The ration of the total section and total acquire times
109   * gives a measure for the lock contention.
110   *
111   * This value may overflow.
112   */
113  uint64_t total_acquire_time;
114
115  /**
116   * @brief The counts of lock acquire operations by contention.
117   *
118   * The contention count for index N corresponds to a lock acquire attempt
119   * with an initial queue length of N.  The last index corresponds to all
120   * lock acquire attempts with an initial queue length greater than or equal
121   * to SMP_LOCK_STATS_CONTENTION_COUNTS minus one.
122   *
123   * The values may overflow.
124   */
125  uint64_t contention_counts[SMP_LOCK_STATS_CONTENTION_COUNTS];
126
127  /**
128   * @brief Total lock section time in CPU counter ticks.
129   *
130   * The average lock section time is the total section time divided by the
131   * lock usage count.
132   *
133   * This value may overflow.
134   */
135  uint64_t total_section_time;
136
137  /**
138   * @brief The lock name.
139   */
140  const char *name;
141#endif /* defined( RTEMS_PROFILING ) */
142} SMP_lock_Stats;
143
144/**
145 * @brief Local context for SMP lock statistics.
146 */
147typedef struct {
148#if defined( RTEMS_PROFILING )
149  /**
150   * @brief The last lock acquire instant in CPU counter ticks.
151   *
152   * This value is used to measure the lock section time.
153   */
154  CPU_Counter_ticks acquire_instant;
155#endif
156} SMP_lock_Stats_context;
157
158/**
159 * @brief SMP lock statistics initializer for static initialization.
160 */
161#if defined( RTEMS_PROFILING )
162#define SMP_LOCK_STATS_INITIALIZER( name ) \
163  { { NULL, NULL }, 0, 0, 0, 0, { 0, 0, 0, 0 }, 0, name }
164#else
165#define SMP_LOCK_STATS_INITIALIZER( name ) \
166  { }
167#endif
168
169/**
170 * @brief Initializes an SMP lock statistics block.
171 *
172 * @param[in, out] stats The SMP lock statistics block.
173 * @param[in] name The name for the SMP lock statistics.  This name must be
174 * persistent throughout the life time of this statistics block.
175 */
176static inline void _SMP_lock_Stats_initialize(
177  SMP_lock_Stats *stats,
178  const char *name
179)
180{
181  SMP_lock_Stats init = SMP_LOCK_STATS_INITIALIZER( name );
182
183  *stats = init;
184}
185
186/**
187 * @brief Destroys an SMP lock statistics block.
188 *
189 * @param[in,out] stats The SMP lock statistics block.
190 */
191static inline void _SMP_lock_Stats_destroy( SMP_lock_Stats *stats );
192
193/**
194 * @brief Destroys an SMP lock statistics block.
195 *
196 * @param[in,out] stats The SMP lock statistics block.
197 * @param[in] stats_context The SMP lock statistics context.
198 */
199static inline void _SMP_lock_Stats_release_update(
200  SMP_lock_Stats *stats,
201  const SMP_lock_Stats_context *stats_context
202);
203
204/**
205 * @brief SMP ticket lock control.
206 */
207typedef struct {
208  Atomic_Uint next_ticket;
209  Atomic_Uint now_serving;
210  SMP_lock_Stats Stats;
211} SMP_ticket_lock_Control;
212
213/**
214 * @brief SMP ticket lock control initializer for static initialization.
215 */
216#define SMP_TICKET_LOCK_INITIALIZER( name ) \
217  { \
218    ATOMIC_INITIALIZER_UINT( 0U ), \
219    ATOMIC_INITIALIZER_UINT( 0U ), \
220    SMP_LOCK_STATS_INITIALIZER( name ) \
221  }
222
223/**
224 * @brief Initializes an SMP ticket lock.
225 *
226 * Concurrent initialization leads to unpredictable results.
227 *
228 * @param[in,out] lock The SMP ticket lock control.
229 * @param[in] name The name for the SMP ticket lock.  This name must be
230 * persistent throughout the life time of this lock.
231 */
232static inline void _SMP_ticket_lock_Initialize(
233  SMP_ticket_lock_Control *lock,
234  const char *name
235)
236{
237  _Atomic_Init_uint( &lock->next_ticket, 0U );
238  _Atomic_Init_uint( &lock->now_serving, 0U );
239  _SMP_lock_Stats_initialize( &lock->Stats, name );
240}
241
242/**
243 * @brief Destroys an SMP ticket lock.
244 *
245 * Concurrent destruction leads to unpredictable results.
246 *
247 * @param[in,out] lock The SMP ticket lock control.
248 */
249static inline void _SMP_ticket_lock_Destroy( SMP_ticket_lock_Control *lock )
250{
251  _SMP_lock_Stats_destroy( &lock->Stats );
252}
253
254/**
255 * @brief Acquires an SMP ticket lock.
256 *
257 * This function will not disable interrupts.  The caller must ensure that the
258 * current thread of execution is not interrupted indefinite once it obtained
259 * the SMP ticket lock.
260 *
261 * @param[in,out] lock The SMP ticket lock control.
262 * @param[out] stats_context The SMP lock statistics context.
263 */
264static inline void _SMP_ticket_lock_Acquire(
265  SMP_ticket_lock_Control *lock,
266  SMP_lock_Stats_context *stats_context
267)
268{
269  unsigned int my_ticket;
270  unsigned int now_serving;
271
272#if defined( RTEMS_PROFILING )
273  SMP_lock_Stats *stats = &lock->Stats;
274  CPU_Counter_ticks first;
275  CPU_Counter_ticks second;
276  CPU_Counter_ticks delta;
277  unsigned int initial_queue_length;
278
279  first = _CPU_Counter_read();
280#endif
281
282  my_ticket =
283    _Atomic_Fetch_add_uint( &lock->next_ticket, 1U, ATOMIC_ORDER_RELAXED );
284
285#if defined( RTEMS_PROFILING )
286  now_serving =
287    _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_ACQUIRE );
288  initial_queue_length = my_ticket - now_serving;
289
290  if ( initial_queue_length > 0 ) {
291#endif
292
293    do {
294      now_serving =
295        _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_ACQUIRE );
296    } while ( now_serving != my_ticket );
297
298#if defined( RTEMS_PROFILING )
299  }
300
301  second = _CPU_Counter_read();
302  stats_context->acquire_instant = second;
303  delta = _CPU_Counter_difference( second, first );
304
305  ++stats->usage_count;
306
307  stats->total_acquire_time += delta;
308
309  if ( stats->max_acquire_time < delta ) {
310    stats->max_acquire_time = delta;
311  }
312
313  if ( initial_queue_length >= SMP_LOCK_STATS_CONTENTION_COUNTS ) {
314    initial_queue_length = SMP_LOCK_STATS_CONTENTION_COUNTS - 1;
315  }
316  ++stats->contention_counts[initial_queue_length];
317#else
318  (void) stats_context;
319#endif
320}
321
322/**
323 * @brief Releases an SMP ticket lock.
324 *
325 * @param[in,out] lock The SMP ticket lock control.
326 * @param[in] stats_context The SMP lock statistics context.
327 */
328static inline void _SMP_ticket_lock_Release(
329  SMP_ticket_lock_Control *lock,
330  const SMP_lock_Stats_context *stats_context
331)
332{
333  unsigned int current_ticket =
334    _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_RELAXED );
335  unsigned int next_ticket = current_ticket + 1U;
336
337  _SMP_lock_Stats_release_update( &lock->Stats, stats_context );
338
339  _Atomic_Store_uint( &lock->now_serving, next_ticket, ATOMIC_ORDER_RELEASE );
340}
341
342/**
343 * @brief SMP lock control.
344 */
345typedef struct {
346  SMP_ticket_lock_Control ticket_lock;
347} SMP_lock_Control;
348
349/**
350 * @brief Local SMP lock context for acquire and release pairs.
351 */
352typedef struct {
353  ISR_Level isr_level;
354  SMP_lock_Stats_context Stats_context;
355} SMP_lock_Context;
356
357/**
358 * @brief SMP lock control initializer for static initialization.
359 */
360#define SMP_LOCK_INITIALIZER( name ) { SMP_TICKET_LOCK_INITIALIZER( name ) }
361
362/**
363 * @brief Initializes an SMP lock.
364 *
365 * Concurrent initialization leads to unpredictable results.
366 *
367 * @param[in,out] lock The SMP lock control.
368 * @param[in] name The name for the SMP lock statistics.  This name must be
369 * persistent throughout the life time of this statistics block.
370 */
371static inline void _SMP_lock_Initialize(
372  SMP_lock_Control *lock,
373  const char *name
374)
375{
376  _SMP_ticket_lock_Initialize( &lock->ticket_lock, name );
377}
378
379/**
380 * @brief Destroys an SMP lock.
381 *
382 * Concurrent destruction leads to unpredictable results.
383 *
384 * @param[in,out] lock The SMP lock control.
385 */
386static inline void _SMP_lock_Destroy( SMP_lock_Control *lock )
387{
388  _SMP_ticket_lock_Destroy( &lock->ticket_lock );
389}
390
391/**
392 * @brief Acquires an SMP lock.
393 *
394 * This function will not disable interrupts.  The caller must ensure that the
395 * current thread of execution is not interrupted indefinite once it obtained
396 * the SMP lock.
397 *
398 * @param[in,out] lock The SMP lock control.
399 * @param[in,out] context The local SMP lock context for an acquire and release
400 * pair.
401 */
402static inline void _SMP_lock_Acquire(
403  SMP_lock_Control *lock,
404  SMP_lock_Context *context
405)
406{
407  (void) context;
408  _SMP_ticket_lock_Acquire( &lock->ticket_lock, &context->Stats_context );
409}
410
411/**
412 * @brief Releases an SMP lock.
413 *
414 * @param[in,out] lock The SMP lock control.
415 * @param[in,out] context The local SMP lock context for an acquire and release
416 * pair.
417 */
418static inline void _SMP_lock_Release(
419  SMP_lock_Control *lock,
420  SMP_lock_Context *context
421)
422{
423  (void) context;
424  _SMP_ticket_lock_Release( &lock->ticket_lock, &context->Stats_context );
425}
426
427/**
428 * @brief Disables interrupts and acquires the SMP lock.
429 *
430 * @param[in,out] lock The SMP lock control.
431 * @param[in,out] context The local SMP lock context for an acquire and release
432 * pair.
433 */
434static inline void _SMP_lock_ISR_disable_and_acquire(
435  SMP_lock_Control *lock,
436  SMP_lock_Context *context
437)
438{
439  _ISR_Disable_without_giant( context->isr_level );
440  _SMP_lock_Acquire( lock, context );
441}
442
443/**
444 * @brief Releases the SMP lock and enables interrupts.
445 *
446 * @param[in,out] lock The SMP lock control.
447 * @param[in,out] context The local SMP lock context for an acquire and release
448 * pair.
449 */
450static inline void _SMP_lock_Release_and_ISR_enable(
451  SMP_lock_Control *lock,
452  SMP_lock_Context *context
453)
454{
455  _SMP_lock_Release( lock, context );
456  _ISR_Enable_without_giant( context->isr_level );
457}
458
459#if defined( RTEMS_PROFILING )
460typedef struct {
461  SMP_lock_Control Lock;
462  Chain_Control Stats_chain;
463  Chain_Control Iterator_chain;
464} SMP_lock_Stats_control;
465
466typedef struct {
467  Chain_Node Node;
468  SMP_lock_Stats *current;
469} SMP_lock_Stats_iteration_context;
470
471extern SMP_lock_Stats_control _SMP_lock_Stats_control;
472
473static inline void _SMP_lock_Stats_iteration_start(
474  SMP_lock_Stats_iteration_context *iteration_context
475)
476{
477  SMP_lock_Stats_control *control = &_SMP_lock_Stats_control;
478  SMP_lock_Context lock_context;
479
480  _SMP_lock_ISR_disable_and_acquire( &control->Lock, &lock_context );
481
482  _Chain_Append_unprotected(
483    &control->Iterator_chain,
484    &iteration_context->Node
485  );
486  iteration_context->current =
487    (SMP_lock_Stats *) _Chain_First( &control->Stats_chain );
488
489  _SMP_lock_Release_and_ISR_enable( &control->Lock, &lock_context );
490}
491
492static inline bool _SMP_lock_Stats_iteration_next(
493  SMP_lock_Stats_iteration_context *iteration_context,
494  SMP_lock_Stats *snapshot,
495  char *name,
496  size_t name_size
497)
498{
499  SMP_lock_Stats_control *control = &_SMP_lock_Stats_control;
500  SMP_lock_Context lock_context;
501  SMP_lock_Stats *current;
502  bool valid;
503
504  _SMP_lock_ISR_disable_and_acquire( &control->Lock, &lock_context );
505
506  current = iteration_context->current;
507  if ( !_Chain_Is_tail( &control->Stats_chain, &current->Node ) ) {
508    size_t name_len = strlen(current->name);
509
510    valid = true;
511
512    iteration_context->current = (SMP_lock_Stats *)
513      _Chain_Next( &current->Node );
514
515    *snapshot = *current;
516    snapshot->name = name;
517
518    if ( name_len >= name_size ) {
519      name_len = name_size - 1;
520    }
521
522    name[name_len] = '\0';
523    memcpy(name, current->name, name_len);
524  } else {
525    valid = false;
526  }
527
528  _SMP_lock_Release_and_ISR_enable( &control->Lock, &lock_context );
529
530  return valid;
531}
532
533static inline void _SMP_lock_Stats_iteration_stop(
534  SMP_lock_Stats_iteration_context *iteration_context
535)
536{
537  SMP_lock_Stats_control *control = &_SMP_lock_Stats_control;
538  SMP_lock_Context lock_context;
539
540  _SMP_lock_ISR_disable_and_acquire( &control->Lock, &lock_context );
541  _Chain_Extract_unprotected( &iteration_context->Node );
542  _SMP_lock_Release_and_ISR_enable( &control->Lock, &lock_context );
543}
544#endif
545
546static inline void _SMP_lock_Stats_destroy( SMP_lock_Stats *stats )
547{
548#if defined( RTEMS_PROFILING )
549  if ( !_Chain_Is_node_off_chain( &stats->Node ) ) {
550    SMP_lock_Stats_control *control = &_SMP_lock_Stats_control;
551    SMP_lock_Context lock_context;
552    SMP_lock_Stats_iteration_context *iteration_context;
553    SMP_lock_Stats_iteration_context *iteration_context_tail;
554    SMP_lock_Stats *next_stats;
555
556    _SMP_lock_ISR_disable_and_acquire( &control->Lock, &lock_context );
557
558    next_stats = (SMP_lock_Stats *) _Chain_Next( &stats->Node );
559    _Chain_Extract_unprotected( &stats->Node );
560
561    iteration_context = (SMP_lock_Stats_iteration_context *)
562      _Chain_First( &control->Iterator_chain );
563    iteration_context_tail = (SMP_lock_Stats_iteration_context *)
564      _Chain_Tail( &control->Iterator_chain );
565
566    while ( iteration_context != iteration_context_tail ) {
567      if ( iteration_context->current == stats ) {
568        iteration_context->current = next_stats;
569      }
570
571      iteration_context = (SMP_lock_Stats_iteration_context *)
572        _Chain_Next( &iteration_context->Node );
573    }
574
575    _SMP_lock_Release_and_ISR_enable( &control->Lock, &lock_context );
576  }
577#else
578  (void) stats;
579#endif
580}
581
582static inline void _SMP_lock_Stats_release_update(
583  SMP_lock_Stats *stats,
584  const SMP_lock_Stats_context *stats_context
585)
586{
587#if defined( RTEMS_PROFILING )
588  CPU_Counter_ticks first = stats_context->acquire_instant;
589  CPU_Counter_ticks second = _CPU_Counter_read();
590  CPU_Counter_ticks delta = _CPU_Counter_difference( second, first );
591
592  stats->total_section_time += delta;
593
594  if ( stats->max_section_time < delta ) {
595    stats->max_section_time = delta;
596
597    if ( _Chain_Is_node_off_chain( &stats->Node ) ) {
598      SMP_lock_Stats_control *control = &_SMP_lock_Stats_control;
599      SMP_lock_Context lock_context;
600
601      _SMP_lock_ISR_disable_and_acquire( &control->Lock, &lock_context );
602      _Chain_Append_unprotected( &control->Stats_chain, &stats->Node );
603      _SMP_lock_Release_and_ISR_enable( &control->Lock, &lock_context );
604    }
605  }
606#else
607  (void) stats;
608  (void) stats_context;
609#endif
610}
611
612/**@}*/
613
614#ifdef __cplusplus
615}
616#endif /* __cplusplus */
617
618#endif /* defined( RTEMS_SMP ) */
619
620#endif /* _RTEMS_SCORE_SMPLOCK_H */
Note: See TracBrowser for help on using the repository browser.