source: rtems/cpukit/score/include/rtems/score/smplock.h @ 372bee84

4.115
Last change on this file since 372bee84 was 372bee84, checked in by Sebastian Huber <sebastian.huber@…>, on 06/26/15 at 13:51:28

score: Simplify _SMP_ticket_lock_Release()

Add a SMP lock statistics pointer to SMP_lock_Stats_context and drop the
SMP lock statistics parameter from _SMP_ticket_lock_Release().

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