1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
|
<table class="head">
<tr>
<td class="head-ltitle">RMLOCK(9)</td>
<td class="head-vol">Kernel Developer's Manual</td>
<td class="head-rtitle">RMLOCK(9)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<p class="Pp"><code class="Nm">rmlock</code>, <code class="Nm">rm_init</code>,
<code class="Nm">rm_init_flags</code>, <code class="Nm">rm_destroy</code>,
<code class="Nm">rm_rlock</code>, <code class="Nm">rm_try_rlock</code>,
<code class="Nm">rm_wlock</code>, <code class="Nm">rm_runlock</code>,
<code class="Nm">rm_wunlock</code>, <code class="Nm">rm_wowned</code>,
<code class="Nm">rm_sleep</code>, <code class="Nm">rm_assert</code>,
<code class="Nm">RM_SYSINIT</code>,
<code class="Nm">RM_SYSINIT_FLAGS</code>, <code class="Nm">rms_init</code>,
<code class="Nm">rms_destroy</code>, <code class="Nm">rms_rlock</code>,
<code class="Nm">rms_wlock</code>, <code class="Nm">rms_runlock</code>,
<code class="Nm">rms_wunlock</code> — <span class="Nd">kernel
reader/writer lock optimized for read-mostly access patterns</span></p>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<p class="Pp"><code class="In">#include
<<a class="In">sys/param.h</a>></code>
<br/>
<code class="In">#include <<a class="In">sys/lock.h</a>></code>
<br/>
<code class="In">#include <<a class="In">sys/rmlock.h</a>></code></p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rm_init</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>, <var class="Fa" style="white-space: nowrap;">const char
*name</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rm_init_flags</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>, <var class="Fa" style="white-space: nowrap;">const char
*name</var>, <var class="Fa" style="white-space: nowrap;">int
opts</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rm_destroy</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rm_rlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>, <var class="Fa" style="white-space: nowrap;">struct
rm_priotracker* tracker</var>);</p>
<p class="Pp"><var class="Ft">int</var>
<br/>
<code class="Fn">rm_try_rlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>, <var class="Fa" style="white-space: nowrap;">struct
rm_priotracker* tracker</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rm_wlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rm_runlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>, <var class="Fa" style="white-space: nowrap;">struct
rm_priotracker* tracker</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rm_wunlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>);</p>
<p class="Pp"><var class="Ft">int</var>
<br/>
<code class="Fn">rm_wowned</code>(<var class="Fa" style="white-space: nowrap;">const
struct rmlock *rm</var>);</p>
<p class="Pp"><var class="Ft">int</var>
<br/>
<code class="Fn">rm_sleep</code>(<var class="Fa" style="white-space: nowrap;">void
*wchan</var>, <var class="Fa" style="white-space: nowrap;">struct rmlock
*rm</var>, <var class="Fa" style="white-space: nowrap;">int priority</var>,
<var class="Fa" style="white-space: nowrap;">const char *wmesg</var>,
<var class="Fa" style="white-space: nowrap;">int timo</var>);</p>
<p class="Pp">
<br/>
<code class="Cd">options INVARIANTS</code>
<br/>
<code class="Cd">options INVARIANT_SUPPORT</code>
<br/>
<var class="Ft">void</var>
<br/>
<code class="Fn">rm_assert</code>(<var class="Fa" style="white-space: nowrap;">struct
rmlock *rm</var>, <var class="Fa" style="white-space: nowrap;">int
what</var>);</p>
<p class="Pp"><code class="In">#include
<<a class="In">sys/kernel.h</a>></code></p>
<p class="Pp"><code class="Fn">RM_SYSINIT</code>(<var class="Fa" style="white-space: nowrap;">name</var>,
<var class="Fa" style="white-space: nowrap;">struct rmlock *rm</var>,
<var class="Fa" style="white-space: nowrap;">const char *desc</var>);</p>
<p class="Pp"><code class="Fn">RM_SYSINIT_FLAGS</code>(<var class="Fa" style="white-space: nowrap;">name</var>,
<var class="Fa" style="white-space: nowrap;">struct rmlock *rm</var>,
<var class="Fa" style="white-space: nowrap;">const char *desc</var>,
<var class="Fa" style="white-space: nowrap;">int flags</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rms_init</code>(<var class="Fa" style="white-space: nowrap;">struct
rmslock *rms</var>, <var class="Fa" style="white-space: nowrap;">const char
*name</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rms_destroy</code>(<var class="Fa" style="white-space: nowrap;">struct
rmslock *rms</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rms_rlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmslock *rms</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rms_wlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmslock *rms</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rms_runlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmslock *rms</var>);</p>
<p class="Pp"><var class="Ft">void</var>
<br/>
<code class="Fn">rms_wunlock</code>(<var class="Fa" style="white-space: nowrap;">struct
rmslock *rms</var>);</p>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
<p class="Pp">Read-mostly locks allow shared access to protected data by
multiple threads, or exclusive access by a single thread. The threads with
shared access are known as
<a class="permalink" href="#readers"><i class="Em" id="readers">readers</i></a>
since they only read the protected data. A thread with exclusive access is
known as a
<a class="permalink" href="#writer"><i class="Em" id="writer">writer</i></a>
since it can modify protected data.</p>
<p class="Pp">Read-mostly locks are designed to be efficient for locks almost
exclusively used as reader locks and as such should be used for protecting
data that rarely changes. Acquiring an exclusive lock after the lock has
been locked for shared access is an expensive operation.</p>
<p class="Pp" id="rm_rlock">Normal read-mostly locks are similar to
<a class="Xr">rwlock(9)</a> locks and follow the same lock ordering rules as
<a class="Xr">rwlock(9)</a> locks. Read-mostly locks have full priority
propagation like mutexes. Unlike <a class="Xr">rwlock(9)</a>, read-mostly
locks propagate priority to both readers and writers. This is implemented
via the <var class="Va">rm_priotracker</var> structure argument supplied to
<a class="permalink" href="#rm_rlock"><code class="Fn">rm_rlock</code></a>()
and
<a class="permalink" href="#rm_runlock"><code class="Fn" id="rm_runlock">rm_runlock</code></a>().
Readers can recurse if the lock is initialized with the
<code class="Dv">RM_RECURSE</code> option; however, writers are never
allowed to recurse.</p>
<p class="Pp" id="rm_init_flags">Sleeping for writers can be allowed by passing
<code class="Dv">RM_SLEEPABLE</code> to
<a class="permalink" href="#rm_init_flags"><code class="Fn">rm_init_flags</code></a>().
It changes lock ordering rules to the same as for <a class="Xr">sx(9)</a>
locks. They do not propagate priority to writers, but they do propagate
priority to readers. Note that readers are not permitted to sleep regardless
of the flag.</p>
<p class="Pp" id="rms_init">Sleepable read-mostly locks (created with
<a class="permalink" href="#rms_init"><code class="Fn">rms_init</code></a>())
allow sleeping for both readers and writers, but don't do priority
propagation for either. They follow <a class="Xr">sx(9)</a> lock
ordering.</p>
<section class="Ss">
<h2 class="Ss" id="Macros_and_Functions"><a class="permalink" href="#Macros_and_Functions">Macros
and Functions</a></h2>
<dl class="Bl-tag">
<dt id="rm_init"><a class="permalink" href="#rm_init"><code class="Fn">rm_init</code></a>(<var class="Fa">struct
rmlock *rm</var>, <var class="Fa">const char *name</var>)</dt>
<dd>Initialize the read-mostly lock <var class="Fa">rm</var>. The
<var class="Fa">name</var> description is used solely for debugging
purposes. This function must be called before any other operations on the
lock.</dd>
<dt><code class="Fn">rm_init_flags</code>(<var class="Fa">struct rmlock
*rm</var>, <var class="Fa">const char *name</var>, <var class="Fa">int
opts</var>)</dt>
<dd>Similar to <code class="Fn">rm_init</code>(), initialize the read-mostly
lock <var class="Fa">rm</var> with a set of optional flags. The
<var class="Fa">opts</var> arguments contains one or more of the following
flags:
<dl class="Bl-tag">
<dt id="RM_NOWITNESS"><a class="permalink" href="#RM_NOWITNESS"><code class="Dv">RM_NOWITNESS</code></a></dt>
<dd>Instruct <a class="Xr">witness(4)</a> to ignore this lock.</dd>
<dt id="RM_RECURSE"><a class="permalink" href="#RM_RECURSE"><code class="Dv">RM_RECURSE</code></a></dt>
<dd>Allow threads to recursively acquire shared locks for
<var class="Fa">rm</var>.</dd>
<dt id="RM_SLEEPABLE"><a class="permalink" href="#RM_SLEEPABLE"><code class="Dv">RM_SLEEPABLE</code></a></dt>
<dd>Create a sleepable read-mostly lock.</dd>
<dt id="RM_NEW"><a class="permalink" href="#RM_NEW"><code class="Dv">RM_NEW</code></a></dt>
<dd>If the kernel has been compiled with <code class="Cd">option
INVARIANTS</code>, <code class="Fn">rm_init_flags</code>() will assert
that the <var class="Fa">rm</var> has not been initialized multiple
times without intervening calls to
<a class="permalink" href="#rm_destroy"><code class="Fn" id="rm_destroy">rm_destroy</code></a>()
unless this option is specified.</dd>
<dt id="RM_DUPOK"><a class="permalink" href="#RM_DUPOK"><code class="Dv">RM_DUPOK</code></a></dt>
<dd><a class="Xr">witness(4)</a> should not log messages about duplicate
locks being acquired.</dd>
</dl>
</dd>
<dt><code class="Fn">rm_rlock</code>(<var class="Fa">struct rmlock *rm</var>,
<var class="Fa">struct rm_priotracker* tracker</var>)</dt>
<dd>Lock <var class="Fa">rm</var> as a reader using
<var class="Fa">tracker</var> to track read owners of a lock for priority
propagation. This data structure is only used internally by
<code class="Nm">rmlock</code> and must persist until
<code class="Fn">rm_runlock</code>() has been called. This data structure
can be allocated on the stack since readers cannot sleep. If any thread
holds this lock exclusively, the current thread blocks, and its priority
is propagated to the exclusive holder. If the lock was initialized with
the <code class="Dv">RM_RECURSE</code> option the
<code class="Fn">rm_rlock</code>() function can be called when the current
thread has already acquired reader access on
<var class="Fa">rm</var>.</dd>
<dt id="rm_try_rlock"><a class="permalink" href="#rm_try_rlock"><code class="Fn">rm_try_rlock</code></a>(<var class="Fa">struct
rmlock *rm</var>, <var class="Fa">struct rm_priotracker* tracker</var>)</dt>
<dd>Try to lock <var class="Fa">rm</var> as a reader.
<code class="Fn">rm_try_rlock</code>() will return 0 if the lock cannot be
acquired immediately; otherwise, the lock will be acquired and a non-zero
value will be returned. Note that <code class="Fn">rm_try_rlock</code>()
may fail even while the lock is not currently held by a writer. If the
lock was initialized with the <code class="Dv">RM_RECURSE</code> option,
<code class="Fn">rm_try_rlock</code>() will succeed if the current thread
has already acquired reader access.</dd>
<dt id="rm_wlock"><a class="permalink" href="#rm_wlock"><code class="Fn">rm_wlock</code></a>(<var class="Fa">struct
rmlock *rm</var>)</dt>
<dd>Lock <var class="Fa">rm</var> as a writer. If there are any shared owners
of the lock, the current thread blocks. The
<code class="Fn">rm_wlock</code>() function cannot be called
recursively.</dd>
<dt><code class="Fn">rm_runlock</code>(<var class="Fa">struct rmlock
*rm</var>, <var class="Fa">struct rm_priotracker* tracker</var>)</dt>
<dd>This function releases a shared lock previously acquired by
<code class="Fn">rm_rlock</code>(). The <var class="Fa">tracker</var>
argument must match the <var class="Fa">tracker</var> argument used for
acquiring the shared lock</dd>
<dt id="rm_wunlock"><a class="permalink" href="#rm_wunlock"><code class="Fn">rm_wunlock</code></a>(<var class="Fa">struct
rmlock *rm</var>)</dt>
<dd>This function releases an exclusive lock previously acquired by
<code class="Fn">rm_wlock</code>().</dd>
<dt><code class="Fn">rm_destroy</code>(<var class="Fa">struct rmlock
*rm</var>)</dt>
<dd>This functions destroys a lock previously initialized with
<code class="Fn">rm_init</code>(). The <var class="Fa">rm</var> lock must
be unlocked.</dd>
<dt id="rm_wowned"><a class="permalink" href="#rm_wowned"><code class="Fn">rm_wowned</code></a>(<var class="Fa">const
struct rmlock *rm</var>)</dt>
<dd>This function returns a non-zero value if the current thread owns an
exclusive lock on <var class="Fa">rm</var>.</dd>
<dt id="rm_sleep"><a class="permalink" href="#rm_sleep"><code class="Fn">rm_sleep</code></a>(<var class="Fa">void
*wchan</var>, <var class="Fa">struct rmlock *rm</var>, <var class="Fa">int
priority</var>, <var class="Fa">const char *wmesg</var>, <var class="Fa">int
timo</var>)</dt>
<dd>This function atomically releases <var class="Fa">rm</var> while waiting
for an event. The <var class="Fa">rm</var> lock must be exclusively
locked. For more details on the parameters to this function, see
<a class="Xr">sleep(9)</a>.</dd>
<dt id="rm_assert"><a class="permalink" href="#rm_assert"><code class="Fn">rm_assert</code></a>(<var class="Fa">struct
rmlock *rm</var>, <var class="Fa">int what</var>)</dt>
<dd>This function asserts that the <var class="Fa">rm</var> lock is in the
state specified by <var class="Fa">what</var>. If the assertions are not
true and the kernel is compiled with <code class="Cd">options
INVARIANTS</code> and <code class="Cd">options INVARIANT_SUPPORT</code>,
the kernel will panic. Currently the following base assertions are
supported:
<dl class="Bl-tag">
<dt id="RA_LOCKED"><a class="permalink" href="#RA_LOCKED"><code class="Dv">RA_LOCKED</code></a></dt>
<dd>Assert that current thread holds either a shared or exclusive lock of
<var class="Fa">rm</var>.</dd>
<dt id="RA_RLOCKED"><a class="permalink" href="#RA_RLOCKED"><code class="Dv">RA_RLOCKED</code></a></dt>
<dd>Assert that current thread holds a shared lock of
<var class="Fa">rm</var>.</dd>
<dt id="RA_WLOCKED"><a class="permalink" href="#RA_WLOCKED"><code class="Dv">RA_WLOCKED</code></a></dt>
<dd>Assert that current thread holds an exclusive lock of
<var class="Fa">rm</var>.</dd>
<dt id="RA_UNLOCKED"><a class="permalink" href="#RA_UNLOCKED"><code class="Dv">RA_UNLOCKED</code></a></dt>
<dd>Assert that current thread holds neither a shared nor exclusive lock
of <var class="Fa">rm</var>.</dd>
</dl>
<p class="Pp">In addition, one of the following optional flags may be
specified with <code class="Dv">RA_LOCKED</code>,
<code class="Dv">RA_RLOCKED</code>, or
<code class="Dv">RA_WLOCKED</code>:</p>
<dl class="Bl-tag">
<dt id="RA_RECURSED"><a class="permalink" href="#RA_RECURSED"><code class="Dv">RA_RECURSED</code></a></dt>
<dd>Assert that the current thread holds a recursive lock of
<var class="Fa">rm</var>.</dd>
<dt id="RA_NOTRECURSED"><a class="permalink" href="#RA_NOTRECURSED"><code class="Dv">RA_NOTRECURSED</code></a></dt>
<dd>Assert that the current thread does not hold a recursive lock of
<var class="Fa">rm</var>.</dd>
</dl>
</dd>
</dl>
<dl class="Bl-tag">
<dt id="rms_init~2"><a class="permalink" href="#rms_init~2"><code class="Fn">rms_init</code></a>(<var class="Fa">struct
rmslock *rms</var>, <var class="Fa">const char *name</var>)</dt>
<dd>Initialize the sleepable read-mostly lock <var class="Fa">rms</var>. The
<var class="Fa">name</var> description is used as
<var class="Fa">wmesg</var> parameter to the <a class="Xr">msleep(9)</a>
routine. This function must be called before any other operations on the
lock.</dd>
<dt id="rms_rlock"><a class="permalink" href="#rms_rlock"><code class="Fn">rms_rlock</code></a>(<var class="Fa">struct
rmlock *rm</var>)</dt>
<dd>Lock <var class="Fa">rms</var> as a reader. If any thread holds this lock
exclusively, the current thread blocks.</dd>
<dt id="rms_wlock"><a class="permalink" href="#rms_wlock"><code class="Fn">rms_wlock</code></a>(<var class="Fa">struct
rmslock *rms</var>)</dt>
<dd>Lock <var class="Fa">rms</var> as a writer. If the lock is already taken,
the current thread blocks. The <code class="Fn">rms_wlock</code>()
function cannot be called recursively.</dd>
<dt id="rms_runlock"><a class="permalink" href="#rms_runlock"><code class="Fn">rms_runlock</code></a>(<var class="Fa">struct
rmslock *rms</var>)</dt>
<dd>This function releases a shared lock previously acquired by
<code class="Fn">rms_rlock</code>().</dd>
<dt id="rms_wunlock"><a class="permalink" href="#rms_wunlock"><code class="Fn">rms_wunlock</code></a>(<var class="Fa">struct
rmslock *rms</var>)</dt>
<dd>This function releases an exclusive lock previously acquired by
<code class="Fn">rms_wlock</code>().</dd>
<dt id="rms_destroy"><a class="permalink" href="#rms_destroy"><code class="Fn">rms_destroy</code></a>(<var class="Fa">struct
rmslock *rms</var>)</dt>
<dd>This functions destroys a lock previously initialized with
<code class="Fn">rms_init</code>(). The <var class="Fa">rms</var> lock
must be unlocked.</dd>
</dl>
</section>
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<p class="Pp"><a class="Xr">locking(9)</a>, <a class="Xr">mutex(9)</a>,
<a class="Xr">panic(9)</a>, <a class="Xr">rwlock(9)</a>,
<a class="Xr">sema(9)</a>, <a class="Xr">sleep(9)</a>,
<a class="Xr">sx(9)</a></p>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<p class="Pp">These functions appeared in <span class="Ux">FreeBSD
7.0</span>.</p>
</section>
<section class="Sh">
<h1 class="Sh" id="AUTHORS"><a class="permalink" href="#AUTHORS">AUTHORS</a></h1>
<p class="Pp">The <code class="Nm">rmlock</code> facility was written by
<span class="An">Stephan Uphoff</span>. This manual page was written by
<span class="An">Gleb Smirnoff</span> for rwlock and modified to reflect
rmlock by <span class="An">Stephan Uphoff</span>.</p>
</section>
<section class="Sh">
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
<p class="Pp">The <code class="Nm">rmlock</code> implementation is currently not
optimized for single processor systems.</p>
<p class="Pp"><code class="Fn">rm_try_rlock</code>() can fail transiently even
when there is no writer, while another reader updates the state on the local
CPU.</p>
<p class="Pp">The <code class="Nm">rmlock</code> implementation uses a single
per CPU list shared by all rmlocks in the system. If rmlocks become popular,
hashing to multiple per CPU queues may be needed to speed up the writer lock
process.</p>
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">April 12, 2021</td>
<td class="foot-os">FreeBSD 15.0</td>
</tr>
</table>
|