Back to home page

OSCL-LXR

 
 

    


0001 ===================================
0002 refcount_t API compared to atomic_t
0003 ===================================
0004 
0005 .. contents:: :local:
0006 
0007 Introduction
0008 ============
0009 
0010 The goal of refcount_t API is to provide a minimal API for implementing
0011 an object's reference counters. While a generic architecture-independent
0012 implementation from lib/refcount.c uses atomic operations underneath,
0013 there are a number of differences between some of the ``refcount_*()`` and
0014 ``atomic_*()`` functions with regards to the memory ordering guarantees.
0015 This document outlines the differences and provides respective examples
0016 in order to help maintainers validate their code against the change in
0017 these memory ordering guarantees.
0018 
0019 The terms used through this document try to follow the formal LKMM defined in
0020 tools/memory-model/Documentation/explanation.txt.
0021 
0022 memory-barriers.txt and atomic_t.txt provide more background to the
0023 memory ordering in general and for atomic operations specifically.
0024 
0025 Relevant types of memory ordering
0026 =================================
0027 
0028 .. note:: The following section only covers some of the memory
0029    ordering types that are relevant for the atomics and reference
0030    counters and used through this document. For a much broader picture
0031    please consult memory-barriers.txt document.
0032 
0033 In the absence of any memory ordering guarantees (i.e. fully unordered)
0034 atomics & refcounters only provide atomicity and
0035 program order (po) relation (on the same CPU). It guarantees that
0036 each ``atomic_*()`` and ``refcount_*()`` operation is atomic and instructions
0037 are executed in program order on a single CPU.
0038 This is implemented using READ_ONCE()/WRITE_ONCE() and
0039 compare-and-swap primitives.
0040 
0041 A strong (full) memory ordering guarantees that all prior loads and
0042 stores (all po-earlier instructions) on the same CPU are completed
0043 before any po-later instruction is executed on the same CPU.
0044 It also guarantees that all po-earlier stores on the same CPU
0045 and all propagated stores from other CPUs must propagate to all
0046 other CPUs before any po-later instruction is executed on the original
0047 CPU (A-cumulative property). This is implemented using smp_mb().
0048 
0049 A RELEASE memory ordering guarantees that all prior loads and
0050 stores (all po-earlier instructions) on the same CPU are completed
0051 before the operation. It also guarantees that all po-earlier
0052 stores on the same CPU and all propagated stores from other CPUs
0053 must propagate to all other CPUs before the release operation
0054 (A-cumulative property). This is implemented using
0055 smp_store_release().
0056 
0057 An ACQUIRE memory ordering guarantees that all post loads and
0058 stores (all po-later instructions) on the same CPU are
0059 completed after the acquire operation. It also guarantees that all
0060 po-later stores on the same CPU must propagate to all other CPUs
0061 after the acquire operation executes. This is implemented using
0062 smp_acquire__after_ctrl_dep().
0063 
0064 A control dependency (on success) for refcounters guarantees that
0065 if a reference for an object was successfully obtained (reference
0066 counter increment or addition happened, function returned true),
0067 then further stores are ordered against this operation.
0068 Control dependency on stores are not implemented using any explicit
0069 barriers, but rely on CPU not to speculate on stores. This is only
0070 a single CPU relation and provides no guarantees for other CPUs.
0071 
0072 
0073 Comparison of functions
0074 =======================
0075 
0076 case 1) - non-"Read/Modify/Write" (RMW) ops
0077 -------------------------------------------
0078 
0079 Function changes:
0080 
0081  * atomic_set() --> refcount_set()
0082  * atomic_read() --> refcount_read()
0083 
0084 Memory ordering guarantee changes:
0085 
0086  * none (both fully unordered)
0087 
0088 
0089 case 2) - increment-based ops that return no value
0090 --------------------------------------------------
0091 
0092 Function changes:
0093 
0094  * atomic_inc() --> refcount_inc()
0095  * atomic_add() --> refcount_add()
0096 
0097 Memory ordering guarantee changes:
0098 
0099  * none (both fully unordered)
0100 
0101 case 3) - decrement-based RMW ops that return no value
0102 ------------------------------------------------------
0103 
0104 Function changes:
0105 
0106  * atomic_dec() --> refcount_dec()
0107 
0108 Memory ordering guarantee changes:
0109 
0110  * fully unordered --> RELEASE ordering
0111 
0112 
0113 case 4) - increment-based RMW ops that return a value
0114 -----------------------------------------------------
0115 
0116 Function changes:
0117 
0118  * atomic_inc_not_zero() --> refcount_inc_not_zero()
0119  * no atomic counterpart --> refcount_add_not_zero()
0120 
0121 Memory ordering guarantees changes:
0122 
0123  * fully ordered --> control dependency on success for stores
0124 
0125 .. note:: We really assume here that necessary ordering is provided as a
0126    result of obtaining pointer to the object!
0127 
0128 
0129 case 5) - generic dec/sub decrement-based RMW ops that return a value
0130 ---------------------------------------------------------------------
0131 
0132 Function changes:
0133 
0134  * atomic_dec_and_test() --> refcount_dec_and_test()
0135  * atomic_sub_and_test() --> refcount_sub_and_test()
0136 
0137 Memory ordering guarantees changes:
0138 
0139  * fully ordered --> RELEASE ordering + ACQUIRE ordering on success
0140 
0141 
0142 case 6) other decrement-based RMW ops that return a value
0143 ---------------------------------------------------------
0144 
0145 Function changes:
0146 
0147  * no atomic counterpart --> refcount_dec_if_one()
0148  * ``atomic_add_unless(&var, -1, 1)`` --> ``refcount_dec_not_one(&var)``
0149 
0150 Memory ordering guarantees changes:
0151 
0152  * fully ordered --> RELEASE ordering + control dependency
0153 
0154 .. note:: atomic_add_unless() only provides full order on success.
0155 
0156 
0157 case 7) - lock-based RMW
0158 ------------------------
0159 
0160 Function changes:
0161 
0162  * atomic_dec_and_lock() --> refcount_dec_and_lock()
0163  * atomic_dec_and_mutex_lock() --> refcount_dec_and_mutex_lock()
0164 
0165 Memory ordering guarantees changes:
0166 
0167  * fully ordered --> RELEASE ordering + control dependency + hold
0168    spin_lock() on success