Commit | Line | Data |
---|---|---|
b6e859f6 ER |
1 | =================================== |
2 | refcount_t API compared to atomic_t | |
3 | =================================== | |
4 | ||
5 | .. contents:: :local: | |
6 | ||
7 | Introduction | |
8 | ============ | |
9 | ||
10 | The goal of refcount_t API is to provide a minimal API for implementing | |
11 | an object's reference counters. While a generic architecture-independent | |
12 | implementation from lib/refcount.c uses atomic operations underneath, | |
13 | there are a number of differences between some of the ``refcount_*()`` and | |
14 | ``atomic_*()`` functions with regards to the memory ordering guarantees. | |
15 | This document outlines the differences and provides respective examples | |
16 | in order to help maintainers validate their code against the change in | |
17 | these memory ordering guarantees. | |
18 | ||
19 | The terms used through this document try to follow the formal LKMM defined in | |
a9251553 | 20 | tools/memory-model/Documentation/explanation.txt. |
b6e859f6 ER |
21 | |
22 | memory-barriers.txt and atomic_t.txt provide more background to the | |
23 | memory ordering in general and for atomic operations specifically. | |
24 | ||
25 | Relevant types of memory ordering | |
26 | ================================= | |
27 | ||
28 | .. note:: The following section only covers some of the memory | |
29 | ordering types that are relevant for the atomics and reference | |
30 | counters and used through this document. For a much broader picture | |
31 | please consult memory-barriers.txt document. | |
32 | ||
33 | In the absence of any memory ordering guarantees (i.e. fully unordered) | |
34 | atomics & refcounters only provide atomicity and | |
35 | program order (po) relation (on the same CPU). It guarantees that | |
36 | each ``atomic_*()`` and ``refcount_*()`` operation is atomic and instructions | |
37 | are executed in program order on a single CPU. | |
38 | This is implemented using :c:func:`READ_ONCE`/:c:func:`WRITE_ONCE` and | |
39 | compare-and-swap primitives. | |
40 | ||
41 | A strong (full) memory ordering guarantees that all prior loads and | |
42 | stores (all po-earlier instructions) on the same CPU are completed | |
43 | before any po-later instruction is executed on the same CPU. | |
44 | It also guarantees that all po-earlier stores on the same CPU | |
45 | and all propagated stores from other CPUs must propagate to all | |
46 | other CPUs before any po-later instruction is executed on the original | |
47 | CPU (A-cumulative property). This is implemented using :c:func:`smp_mb`. | |
48 | ||
49 | A RELEASE memory ordering guarantees that all prior loads and | |
50 | stores (all po-earlier instructions) on the same CPU are completed | |
51 | before the operation. It also guarantees that all po-earlier | |
52 | stores on the same CPU and all propagated stores from other CPUs | |
53 | must propagate to all other CPUs before the release operation | |
54 | (A-cumulative property). This is implemented using | |
55 | :c:func:`smp_store_release`. | |
56 | ||
57 | A control dependency (on success) for refcounters guarantees that | |
58 | if a reference for an object was successfully obtained (reference | |
59 | counter increment or addition happened, function returned true), | |
60 | then further stores are ordered against this operation. | |
61 | Control dependency on stores are not implemented using any explicit | |
62 | barriers, but rely on CPU not to speculate on stores. This is only | |
63 | a single CPU relation and provides no guarantees for other CPUs. | |
64 | ||
65 | ||
66 | Comparison of functions | |
67 | ======================= | |
68 | ||
69 | case 1) - non-"Read/Modify/Write" (RMW) ops | |
70 | ------------------------------------------- | |
71 | ||
72 | Function changes: | |
73 | ||
74 | * :c:func:`atomic_set` --> :c:func:`refcount_set` | |
75 | * :c:func:`atomic_read` --> :c:func:`refcount_read` | |
76 | ||
77 | Memory ordering guarantee changes: | |
78 | ||
79 | * none (both fully unordered) | |
80 | ||
81 | ||
82 | case 2) - increment-based ops that return no value | |
83 | -------------------------------------------------- | |
84 | ||
85 | Function changes: | |
86 | ||
87 | * :c:func:`atomic_inc` --> :c:func:`refcount_inc` | |
88 | * :c:func:`atomic_add` --> :c:func:`refcount_add` | |
89 | ||
90 | Memory ordering guarantee changes: | |
91 | ||
92 | * none (both fully unordered) | |
93 | ||
94 | case 3) - decrement-based RMW ops that return no value | |
95 | ------------------------------------------------------ | |
96 | ||
97 | Function changes: | |
98 | ||
99 | * :c:func:`atomic_dec` --> :c:func:`refcount_dec` | |
100 | ||
101 | Memory ordering guarantee changes: | |
102 | ||
103 | * fully unordered --> RELEASE ordering | |
104 | ||
105 | ||
106 | case 4) - increment-based RMW ops that return a value | |
107 | ----------------------------------------------------- | |
108 | ||
109 | Function changes: | |
110 | ||
111 | * :c:func:`atomic_inc_not_zero` --> :c:func:`refcount_inc_not_zero` | |
112 | * no atomic counterpart --> :c:func:`refcount_add_not_zero` | |
113 | ||
114 | Memory ordering guarantees changes: | |
115 | ||
116 | * fully ordered --> control dependency on success for stores | |
117 | ||
118 | .. note:: We really assume here that necessary ordering is provided as a | |
119 | result of obtaining pointer to the object! | |
120 | ||
121 | ||
122 | case 5) - decrement-based RMW ops that return a value | |
123 | ----------------------------------------------------- | |
124 | ||
125 | Function changes: | |
126 | ||
127 | * :c:func:`atomic_dec_and_test` --> :c:func:`refcount_dec_and_test` | |
128 | * :c:func:`atomic_sub_and_test` --> :c:func:`refcount_sub_and_test` | |
129 | * no atomic counterpart --> :c:func:`refcount_dec_if_one` | |
130 | * ``atomic_add_unless(&var, -1, 1)`` --> ``refcount_dec_not_one(&var)`` | |
131 | ||
132 | Memory ordering guarantees changes: | |
133 | ||
134 | * fully ordered --> RELEASE ordering + control dependency | |
135 | ||
136 | .. note:: :c:func:`atomic_add_unless` only provides full order on success. | |
137 | ||
138 | ||
139 | case 6) - lock-based RMW | |
140 | ------------------------ | |
141 | ||
142 | Function changes: | |
143 | ||
144 | * :c:func:`atomic_dec_and_lock` --> :c:func:`refcount_dec_and_lock` | |
145 | * :c:func:`atomic_dec_and_mutex_lock` --> :c:func:`refcount_dec_and_mutex_lock` | |
146 | ||
147 | Memory ordering guarantees changes: | |
148 | ||
149 | * fully ordered --> RELEASE ordering + control dependency + hold | |
150 | :c:func:`spin_lock` on success |